pax_global_header00006660000000000000000000000064152024367200014513gustar00rootroot0000000000000052 comment=1c4b88a90504bf43f2fafda671d2c9b2cd941f2a golang-github-cilium-ebpf-0.21.0+ds1/000077500000000000000000000000001520243672000171775ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/.clang-format000066400000000000000000000015371520243672000215600ustar00rootroot00000000000000--- Language: Cpp BasedOnStyle: LLVM AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: true AlignEscapedNewlines: DontAlign # mkdocs annotations in source code are written as trailing comments # and alignment pushes these really far away from the content. AlignTrailingComments: false AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortFunctionsOnASingleLine: false BreakBeforeBraces: Attach IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: false TabWidth: 4 UseTab: ForContinuationAndIndentation ColumnLimit: 1000 # Go compiler comments need to stay unindented. CommentPragmas: '^go:.*' # linux/bpf.h needs to be included before bpf/bpf_helpers.h for types like __u64 # and sorting makes this impossible. SortIncludes: false ... golang-github-cilium-ebpf-0.21.0+ds1/.gitattributes000066400000000000000000000002141520243672000220670ustar00rootroot00000000000000# Force line ending normalisation * text=auto # Show types.go in the PR diff view by default internal/sys/types.go linguist-generated=false golang-github-cilium-ebpf-0.21.0+ds1/.github/000077500000000000000000000000001520243672000205375ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001520243672000227225ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000015321520243672000256160ustar00rootroot00000000000000name: Bug report description: Create a report to help us improve labels: ["bug"] assignees: [] body: - type: markdown attributes: value: "Thank you for reporting a bug. Please fill out the fields below." - type: textarea attributes: label: Describe the bug description: | A clear and concise description of what the bug is. Include what you expected to happen instead. validations: required: true - type: textarea attributes: label: How to reproduce description: "Steps to reproduce the behavior." validations: required: true - type: input id: version attributes: label: Version information description: The output of `go list -m github.com/cilium/ebpf`. placeholder: github.com/cilium/ebpf vX.Y.Z validations: required: true golang-github-cilium-ebpf-0.21.0+ds1/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000003651520243672000247160ustar00rootroot00000000000000contact_links: - name: Questions url: https://github.com/cilium/ebpf/discussions/categories/q-a about: Please ask and answer questions here. - name: Slack url: https://cilium.slack.com/messages/ebpf-go about: Join our slack. golang-github-cilium-ebpf-0.21.0+ds1/.github/dependabot.yml000066400000000000000000000007731520243672000233760ustar00rootroot00000000000000--- version: 2 updates: - package-ecosystem: "pip" directory: "/docs" schedule: interval: "monthly" allow: # Only manage direct dependencies in Pipfile, ignore transient # dependencies only appearing in Pipfile.lock. - dependency-name: "*" dependency-type: "direct" groups: docs: dependency-type: production applies-to: version-updates - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" golang-github-cilium-ebpf-0.21.0+ds1/.github/workflows/000077500000000000000000000000001520243672000225745ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/.github/workflows/apidiff.yml000066400000000000000000000014731520243672000247260ustar00rootroot00000000000000name: apidiff on: push: branches: [ "main" ] pull_request: branches: [ "main" ] jobs: go-apidiff: name: go-apidiff runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Run go-apidiff id: apidiff continue-on-error: true uses: joelanford/go-apidiff@main - name: Create apidiff.json run: | echo '{"id": ${{ github.event.pull_request.number }}, "semver-type": "${{ steps.apidiff.outputs.semver-type }}"}' > apidiff.json - name: Upload apidiff.json uses: actions/upload-artifact@v7 with: name: apidiff path: apidiff.json golang-github-cilium-ebpf-0.21.0+ds1/.github/workflows/ci.yml000066400000000000000000000337631520243672000237260ustar00rootroot00000000000000name: ci on: push: branches: [ "main" ] pull_request: branches: [ "main" ] env: TMPDIR: /tmp CI_MAX_KERNEL_VERSION: '6.18' CI_MAX_EFW_VERSION: '1.0.0-rc2' CI_MIN_CLANG_VERSION: '13' go_version: '~1.25' prev_go_version: '~1.24' CGO_ENABLED: '0' # Sync with Pipfile and netlify.toml. python_version: '~3.13' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build-and-lint: name: Build and Lint runs-on: ubuntu-22.04 timeout-minutes: 10 steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '${{ env.go_version }}' - name: Run staticcheck uses: dominikh/staticcheck-action@v1 with: version: "master" install-go: false - name: Run golangci-lint uses: golangci/golangci-lint-action@v9.2.0 - name: Generate and format code run: | make clean && make container-all if ! git diff --exit-code; then echo "found unformatted source files, or generated files are not up to date, run 'make'" >&2 exit 1 fi - name: Test bpf2go run: | go test -v ./cmd/bpf2go - name: Build run: go build -v ./... cross-build: name: Cross build runs-on: ubuntu-22.04 needs: build-and-lint timeout-minutes: 10 steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '${{ env.go_version }}' - name: Cross build darwin env: GOOS: darwin run: | go build -v ./... go test -c -o /dev/null ./... >/dev/null - name: Cross build arm32 env: GOARCH: arm GOARM: 6 run: | go build -v ./... go test -c -o /dev/null ./... >/dev/null - name: Cross build wasm env: GOOS: js GOARCH: wasm run: | go build -v ./... go test -c -o /dev/null ./... >/dev/null build-docs: name: Build Documentation runs-on: ubuntu-22.04 timeout-minutes: 10 steps: - uses: actions/checkout@v6 with: # The mkdocs git-authors plugin needs access to the full revision # history to correctly generate its statistics. fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '${{ env.go_version }}' - name: Set up Python uses: actions/setup-python@v6 with: python-version: '${{ env.python_version }}' cache: 'pipenv' - name: Install pipenv run: pip3 install pipenv - name: Install Dependencies run: pipenv install working-directory: ./docs - name: Build Documentation run: make build working-directory: ./docs test-on-prev-go: name: Run tests on previous stable Go runs-on: ubuntu-latest needs: build-and-lint timeout-minutes: 15 env: CI_KERNEL_SELFTESTS: '/usr/src/linux/tools/testing/selftests/bpf' steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '${{ env.prev_go_version }}' - run: go install lmb.io/vimto@latest - run: go install gotest.tools/gotestsum@v1.12.3 - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-x86 - run: sudo chmod 0666 /dev/kvm - name: Test env: GOTRACEBACK: crash CGO_ENABLED: 1 # CGo is required by `-race` run: | gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml -- vimto -kernel :stable-selftests -- go test -race -timeout 5m -short -count 1 -json ./... - name: Benchmark run: vimto -kernel :stable-selftests -- go test -short -run '^$' -bench . -benchtime=1x ./... - name: Upload coredumps uses: actions/upload-artifact@v7 if: ${{ failure() }} with: name: cores if-no-files-found: ignore path: | **/core-* **/*.test - name: Upload Test Results if: always() uses: actions/upload-artifact@v7 with: name: Test Results (previous stable Go) path: junit.xml test-on-arm64: name: Run tests on arm64 runs-on: ubuntu-24.04-arm64 needs: build-and-lint timeout-minutes: 15 env: EBPF_TEST_IGNORE_VERSION: 'TestKprobeMulti,TestKprobeMultiErrors,TestKprobeMultiCookie,TestKprobeMultiProgramCall,TestHaveBPFLinkKprobeMulti,TestKprobeSession,TestHaveBPFLinkKprobeSession,TestHaveProgramType/LircMode2' steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '${{ env.go_version }}' - run: go install gotest.tools/gotestsum@v1.12.3 - name: Test # Skip TestGoarches/loong64 because the GH arm64 Go toolchain seems to be weird. # Ubuntu 24.04 crashes when executing TestKfunc. run: gotestsum --ignore-non-json-output-lines --junitfile junit.xml -- -exec 'sudo -E' -short -count 1 -skip '^TestGoarches/loong64$' -skip '^TestKfunc$' -json ./... - name: Benchmark run: go test -exec sudo -short -run '^$' -bench . -benchtime=1x ./... - name: Upload Test Results if: always() uses: actions/upload-artifact@v7 with: name: Test Results (arm64) path: junit.xml - name: Show dmesg if: failure() run: | sudo dmesg linux-test: name: Run tests (Linux) runs-on: ubuntu-latest needs: build-and-lint timeout-minutes: 15 strategy: matrix: tag: - "mainline" - "stable" - "6.12" - "6.6" - "6.1" - "5.15" - "5.10" - "5.4" steps: - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '${{ env.go_version }}' - run: go install gotest.tools/gotestsum@v1.12.3 - run: go install lmb.io/vimto@latest - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-x86 - run: sudo chmod 0666 /dev/kvm - name: Test run: gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml -- vimto -kernel :${{ matrix.tag }} -- go test -short -count 1 -json ./... - name: Upload Test Results if: always() uses: actions/upload-artifact@v7 with: name: Test Results (linux ${{ matrix.tag }}) path: junit.xml windows-test: name: Run tests (Windows) runs-on: windows-2022 needs: build-and-lint timeout-minutes: 15 strategy: matrix: version: - "main" - "1.0.0-rc1" env: # Fix slow Go compile and cache restore # See https://github.com/actions/setup-go/pull/515 GOCACHE: D:\gocache GOMODCACHE: D:\gomodcache # Avoid putting temp on slow C: TEMP: D:\temp CI_EFW_VERSION: "0.21.0" steps: - run: mkdir D:\temp shell: pwsh - name: Get eBPF for Windows download URL id: determine-url uses: actions/github-script@v8 with: script: | if ("${{ matrix.version }}" != "main") { // Use version from matrix to fetch from release const version = "${{ matrix.version }}"; const releaseTag = `Release-v${version}`; console.log(`Fetching release: ${releaseTag}`); // Get the release by tag const release = await github.rest.repos.getReleaseByTag({ owner: 'microsoft', repo: 'ebpf-for-windows', tag: releaseTag }); if (!release.data) { core.setFailed(`Release ${releaseTag} not found`); return; } console.log(`Found release: ${release.data.name}`); // Find the Build.Debug.x64.zip asset const assetName = 'Build.Debug.x64.zip'; const asset = release.data.assets.find(a => a.name === assetName); if (!asset) { console.log('Available assets:', release.data.assets.map(a => a.name)); core.setFailed(`${assetName} asset not found in release ${releaseTag}`); return; } const download_url = asset.browser_download_url; console.log(`Download URL: ${download_url}`); core.setOutput('download_url', download_url); return } // Get the latest successful merge_group run const workflow_runs = await github.rest.actions.listWorkflowRuns({ owner: 'microsoft', repo: 'ebpf-for-windows', workflow_id: 'cicd.yml', event: 'schedule', branch: 'main', status: 'completed', per_page: 1 }); if (workflow_runs.data.workflow_runs.length === 0) { core.setFailed('No successful merge_group workflow runs found'); return; } // Get artifacts from this run const run_id = workflow_runs.data.workflow_runs[0].id; const run_url = workflow_runs.data.workflow_runs[0].html_url; console.log(`Using workflow run: ${run_url}`); // Paginate through all artifacts let allArtifacts = []; let page = 1; while (true) { const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: 'microsoft', repo: 'ebpf-for-windows', run_id: run_id, per_page: 100, page: page }); allArtifacts = allArtifacts.concat(artifacts.data.artifacts); // If we got fewer than 100, we've reached the last page if (artifacts.data.artifacts.length < 100) break; page++; } // Find the specific artifact const artifact = allArtifacts.find(a => a.name === 'Build-x64-Debug'); if (!artifact) { console.log('Available artifacts:', artifacts.data.artifacts.map(a => a.name)); core.setFailed('Build-x64-Debug artifact not found in the workflow run'); return; } // Get the download URL via redirect const response = await github.rest.actions.downloadArtifact({ owner: 'microsoft', repo: 'ebpf-for-windows', artifact_id: artifact.id, archive_format: 'zip', request: { redirect: 'manual' } }); // Extract the location header which contains the actual download URL const download_url = response.url; if (!download_url) { core.setFailed('Failed to get redirect URL from headers'); return; } core.setOutput('download_url', download_url); - name: Download and Install eBPF for Windows shell: pwsh run: | Invoke-WebRequest -Uri "${{ steps.determine-url.outputs.download_url }}" -OutFile "$env:TEMP\efw.zip" if ("${{ matrix.version }}" -eq "main") { # Workflow artifact has nested structure: outer zip contains build-Debug.zip Expand-Archive -Path "$env:TEMP\efw.zip" -DestinationPath "$env:TEMP" Expand-Archive -Path "$env:TEMP\build-Debug.zip" -DestinationPath "$env:TEMP\ebpf" } else { # Release asset is the final zip, extract directly Expand-Archive -Path "$env:TEMP\efw.zip" -DestinationPath "$env:TEMP\ebpf" } $setupScript = Get-ChildItem -Path "$env:TEMP\ebpf" -Filter "install_ebpf.psm1" -Recurse | Select-Object -First 1 if ($setupScript) { Write-Host "Found setup script: $($setupScript.FullName)" $releasePath = "$env:TEMP\ebpf\release" Rename-Item -Path $setupScript.DirectoryName -NewName $releasePath Write-Host "Renamed directory to: $releasePath" Set-Location -Path $releasePath Write-Host "Changed directory to: $(Get-Location)" Import-Module .\\install_ebpf.psm1 -ArgumentList ($pwd, "install.log") -Force Get-PSExec Install-eBPFComponents -KmTracing $false -KmTraceType "file" -TestMode "Normal" } else { Write-Error "Setup script not found in the extracted package" exit 1 } - name: Add eBPF for Windows to PATH shell: pwsh run: echo "C:\Program Files\ebpf-for-windows\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version: '${{ env.go_version }}' - run: go install gotest.tools/gotestsum@v1.12.3 - name: Test run: > gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml -- go test -short -count 1 -json ./... - name: Upload Test Results if: always() uses: actions/upload-artifact@v7 with: name: Test Results (windows ${{ matrix.version }}) path: junit.xml results: name: Results runs-on: ubuntu-latest needs: - build-and-lint - cross-build - build-docs - test-on-prev-go - test-on-arm64 - linux-test - windows-test if: always() steps: - name: Check Results run: | if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then echo "Some checks failed" exit 1 else echo "All checks passed successfully" fi golang-github-cilium-ebpf-0.21.0+ds1/.github/workflows/trusted.yml000066400000000000000000000045111520243672000250120ustar00rootroot00000000000000on: workflow_run: workflows: ["apidiff"] types: - completed permissions: pull-requests: write jobs: tag-breaking-change: name: Tag breaking changes runs-on: ubuntu-latest if: github.event.workflow_run.event == 'pull_request' steps: - name: 'Download artifact' uses: actions/github-script@v8 with: script: | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{github.event.workflow_run.id }}, }); var matchArtifact = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "apidiff" })[0]; var download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifact.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/apidiff.zip', Buffer.from(download.data)); - run: unzip apidiff.zip - name: 'Add or remove label' uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | var fs = require('fs'); var jsonData = JSON.parse(fs.readFileSync('apidiff.json', 'utf8')); var issueNumber = jsonData.id; var semverType = jsonData["semver-type"]; if (semverType === 'major') { // Add 'breaking-change' label await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, labels: ['breaking-change'] }); } else { // Remove 'breaking-change' label if it exists try { await github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, name: 'breaking-change' }); } catch (error) { console.log('Label breaking-change not found or already removed'); } }golang-github-cilium-ebpf-0.21.0+ds1/.gitignore000066400000000000000000000003161520243672000211670ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib *.o !*_bpf*.o # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out golang-github-cilium-ebpf-0.21.0+ds1/.golangci.yaml000066400000000000000000000010411520243672000217200ustar00rootroot00000000000000version: "2" linters: default: none enable: - depguard - govet - ineffassign - misspell - unused settings: depguard: rules: no-x-sys-unix: files: - '!**/internal/unix/*.go' - '!**/examples/**/*.go' - '!**/docs/**/*.go' deny: - pkg: golang.org/x/sys/unix desc: use internal/unix instead formatters: enable: - gofmt - goimports settings: goimports: local-prefixes: - github.com/cilium/ebpf golang-github-cilium-ebpf-0.21.0+ds1/.vimto.toml000066400000000000000000000004041520243672000213060ustar00rootroot00000000000000kernel="ghcr.io/cilium/ci-kernels:stable" smp="cpus=2" memory="1G" user="root" setup=[ "mount -t cgroup2 -o nosuid,noexec,nodev cgroup2 /sys/fs/cgroup", "/bin/sh -c 'modprobe bpf_testmod || true'", "dmesg --clear", ] teardown=[ "dmesg --read-clear", ] golang-github-cilium-ebpf-0.21.0+ds1/CODEOWNERS000066400000000000000000000005741520243672000206000ustar00rootroot00000000000000* @cilium/ebpf-lib-maintainers /features/ @rgo3 /link/ @mmat11 /perf/ @florianl /ringbuf/ @florianl /btf/ @dylandreimerink /docs/ @ti-mo # Windows specific code. /docs/**/windows*.md @cilium/ebpf-go-windows-reviewers /internal/efw @cilium/ebpf-go-windows-reviewers windows/ @cilium/ebpf-go-windows-reviewers # Folders *windows*.go @cilium/ebpf-go-windows-reviewers # Go code golang-github-cilium-ebpf-0.21.0+ds1/CODE_OF_CONDUCT.md000066400000000000000000000062551520243672000220060ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In 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. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project 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. Project 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. ## Scope This 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. ## Enforcement Instances 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. Project 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. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ golang-github-cilium-ebpf-0.21.0+ds1/CONTRIBUTING.md000066400000000000000000000003001520243672000214210ustar00rootroot00000000000000# Contributing to ebpf-go Want to contribute to ebpf-go? There are a few things you need to know. We wrote a [contribution guide](https://ebpf-go.dev/contributing/) to help you get started. golang-github-cilium-ebpf-0.21.0+ds1/LICENSE000066400000000000000000000021661520243672000202110ustar00rootroot00000000000000MIT License Copyright (c) 2017 Nathan Sweet Copyright (c) 2018, 2019 Cloudflare Copyright (c) 2019 Authors of Cilium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-cilium-ebpf-0.21.0+ds1/MAINTAINERS.md000066400000000000000000000002151520243672000212710ustar00rootroot00000000000000# Maintainers Maintainers can be found in the [Cilium Maintainers file](https://github.com/cilium/community/blob/main/roles/Maintainers.md) golang-github-cilium-ebpf-0.21.0+ds1/Makefile000066400000000000000000000102361520243672000206410ustar00rootroot00000000000000# The development version of clang is distributed as the 'clang' binary, # while stable/released versions have a version number attached. # Pin the default clang to a stable version. CLANG ?= clang-20 STRIP ?= llvm-strip-20 OBJCOPY ?= llvm-objcopy-20 CFLAGS := -O2 -g -Wall -Werror -mcpu=v2 $(CFLAGS) CI_KERNEL_URL ?= https://github.com/cilium/ci-kernels/raw/master/ # Obtain an absolute path to the directory of the Makefile. # Assume the Makefile is in the root of the repository. REPODIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) # Prefer podman if installed, otherwise use docker. # Note: Setting the var at runtime will always override. CONTAINER_ENGINE ?= $(if $(shell command -v podman),podman,docker) # Configure container runtime arguments based on the container engine. CONTAINER_RUN_ARGS := \ --env MAKEFLAGS \ --env BPF2GO_CC="$(CLANG)" \ --env BPF2GO_CFLAGS="$(CFLAGS)" \ --env HOME=/tmp \ -v "${REPODIR}":/ebpf -w /ebpf \ -v "$(shell go env GOCACHE)":/tmp/.cache/go-build \ -v "$(shell go env GOPATH)":/go \ -v "$(shell go env GOMODCACHE)":/go/pkg/mod ifeq ($(CONTAINER_ENGINE), podman) CONTAINER_RUN_ARGS += --log-driver=none --security-opt label=disable else CONTAINER_RUN_ARGS += --user "$(shell stat -c '%u:%g' ${REPODIR})" endif IMAGE := $(shell cat ${REPODIR}/testdata/docker/IMAGE) VERSION := $(shell cat ${REPODIR}/testdata/docker/VERSION) TARGETS_EL := \ testdata/linked1 \ testdata/linked2 \ testdata/linked TARGETS := \ testdata/loader-clang-14 \ testdata/loader-clang-17 \ testdata/loader-$(CLANG) \ testdata/loader_nobtf \ testdata/manyprogs \ testdata/btf_map_init \ testdata/invalid_map \ testdata/raw_tracepoint \ testdata/invalid_map_static \ testdata/invalid_btf_map_init \ testdata/strings \ testdata/freplace \ testdata/fentry_fexit \ testdata/iproute2_map_compat \ testdata/map_spin_lock \ testdata/subprog_reloc \ testdata/fwd_decl \ testdata/kconfig \ testdata/ksym \ testdata/kfunc \ testdata/invalid-kfunc \ testdata/kfunc-kmod \ testdata/constants \ testdata/errors \ testdata/variables \ testdata/arena \ testdata/struct_ops \ btf/testdata/relocs \ btf/testdata/relocs_read \ btf/testdata/relocs_read_tgt \ btf/testdata/relocs_enum \ btf/testdata/tags \ cmd/bpf2go/testdata/minimal HEADERS := $(wildcard testdata/*.h) .PHONY: all clean container-all container-shell generate .DEFAULT_TARGET = container-all # Build all ELF binaries using a containerized LLVM toolchain. container-all: +${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \ "${IMAGE}:${VERSION}" \ $(MAKE) all # (debug) Drop the user into a shell inside the container as root. # Set BPF2GO_ envs to make 'make generate' just work. container-shell: ${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \ "${IMAGE}:${VERSION}" clean: find "$(CURDIR)" -name "*.elf" -delete find "$(CURDIR)" -name "*.o" -delete format: find . -type f -name "*.c" | xargs clang-format -i all: format testdata update-external-deps ln -srf testdata/loader-$(CLANG)-el.elf testdata/loader-el.elf ln -srf testdata/loader-$(CLANG)-eb.elf testdata/loader-eb.elf $(MAKE) generate generate: go generate -run "stringer" ./... go generate -run "gentypes" ./... go generate -skip "(gentypes|stringer)" ./... testdata: $(addsuffix -el.elf,$(TARGETS)) $(addsuffix -eb.elf,$(TARGETS)) $(addsuffix -el.elf,$(TARGETS_EL)) testdata/loader-%-el.elf: testdata/loader.c $(HEADERS) $* $(CFLAGS) -target bpfel -c $< -o $@ $(STRIP) -g $@ testdata/loader-%-eb.elf: testdata/loader.c $(HEADERS) $* $(CFLAGS) -target bpfeb -c $< -o $@ $(STRIP) -g $@ testdata/loader_nobtf-el.elf: testdata/loader.c $(HEADERS) $(CLANG) $(CFLAGS) -g0 -D__NOBTF__ -target bpfel -c $< -o $@ testdata/loader_nobtf-eb.elf: testdata/loader.c $(HEADERS) $(CLANG) $(CFLAGS) -g0 -D__NOBTF__ -target bpfeb -c $< -o $@ %-el.elf: %.c $(HEADERS) $(CLANG) $(CFLAGS) -target bpfel -c $< -o $@ $(STRIP) -g $@ %-eb.elf: %.c $(HEADERS) $(CLANG) $(CFLAGS) -target bpfeb -c $< -o $@ $(STRIP) -g $@ testdata/linked-el.elf: testdata/linked1-el.elf testdata/linked2-el.elf bpftool gen object $@ $^ .PHONY: update-external-deps update-external-deps: ./scripts/update-kernel-deps.sh ./scripts/update-efw-deps.sh golang-github-cilium-ebpf-0.21.0+ds1/README.md000066400000000000000000000064531520243672000204660ustar00rootroot00000000000000# eBPF [![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf) ![HoneyGopher](docs/ebpf/ebpf-go.png) ebpf-go is a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs. It has minimal external dependencies and is intended to be used in long running processes. See [ebpf.io](https://ebpf.io) for complementary projects from the wider eBPF ecosystem. ## Getting Started Please take a look at our [Getting Started] guide. [Contributions](https://ebpf-go.dev/contributing) are highly encouraged, as they highlight certain use cases of eBPF and the library, and help shape the future of the project. ## Getting Help The community actively monitors our [GitHub Discussions](https://github.com/cilium/ebpf/discussions) page. Please search for existing threads before starting a new one. Refrain from opening issues on the bug tracker if you're just starting out or if you're not sure if something is a bug in the library code. Alternatively, [join](https://ebpf.io/slack) the [#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack if you have other questions regarding the project. Note that this channel is ephemeral and has its history erased past a certain point, which is less helpful for others running into the same problem later. ## Packages This library includes the following packages: * [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic assembler, allowing you to write eBPF assembly instructions directly within your Go code. (You don't need to use this if you prefer to write your eBPF program in C.) * [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows compiling and embedding eBPF programs written in C within Go code. As well as compiling the C code, it auto-generates Go code for loading and manipulating the eBPF program and map objects. * [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF to various hooks * [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a `PERF_EVENT_ARRAY` * [ringbuf](https://pkg.go.dev/github.com/cilium/ebpf/ringbuf) allows reading from a `BPF_MAP_TYPE_RINGBUF` map * [features](https://pkg.go.dev/github.com/cilium/ebpf/features) implements the equivalent of `bpftool feature probe` for discovering BPF-related kernel features using native Go. * [rlimit](https://pkg.go.dev/github.com/cilium/ebpf/rlimit) provides a convenient API to lift the `RLIMIT_MEMLOCK` constraint on kernels before 5.11. * [btf](https://pkg.go.dev/github.com/cilium/ebpf/btf) allows reading the BPF Type Format. * [pin](https://pkg.go.dev/github.com/cilium/ebpf/pin) provides APIs for working with pinned objects on bpffs. ## Requirements * A version of Go that is [supported by upstream](https://golang.org/doc/devel/release.html#policy) * Linux (amd64, arm64): CI is run against kernel.org LTS releases. >= 4.4 should work but EOL'ed versions are not supported. * Windows (amd64): CI is run against Windows Server 2022. Only the latest eBPF for Windows release is supported. * Other architectures are best effort. 32bit arches are not supported. ## License MIT ### eBPF Gopher The eBPF honeygopher is based on the Go gopher designed by Renee French. [Getting Started]: https://ebpf-go.dev/guides/getting-started/ golang-github-cilium-ebpf-0.21.0+ds1/asm/000077500000000000000000000000001520243672000177575ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/asm/alu.go000066400000000000000000000100201520243672000210600ustar00rootroot00000000000000package asm //go:generate go tool stringer -output alu_string.go -type=Source,Endianness,ALUOp // Source of ALU / ALU64 / Branch operations // // msb lsb // +------------+-+---+ // | op |S|cls| // +------------+-+---+ type Source uint16 const sourceMask OpCode = 0x0008 // Source bitmask const ( // InvalidSource is returned by getters when invoked // on non ALU / branch OpCodes. InvalidSource Source = 0xffff // ImmSource src is from constant ImmSource Source = 0x0000 // RegSource src is from register RegSource Source = 0x0008 ) // The Endianness of a byte swap instruction. type Endianness uint8 const endianMask = sourceMask // Endian flags const ( InvalidEndian Endianness = 0xff // Convert to little endian LE Endianness = 0x00 // Convert to big endian BE Endianness = 0x08 ) // ALUOp are ALU / ALU64 operations // // msb lsb // +-------+----+-+---+ // | EXT | OP |s|cls| // +-------+----+-+---+ type ALUOp uint16 const aluMask OpCode = 0x3ff0 const ( // InvalidALUOp is returned by getters when invoked // on non ALU OpCodes InvalidALUOp ALUOp = 0xffff // Add - addition Add ALUOp = 0x0000 // Sub - subtraction Sub ALUOp = 0x0010 // Mul - multiplication Mul ALUOp = 0x0020 // Div - division Div ALUOp = 0x0030 // SDiv - signed division SDiv ALUOp = Div + 0x0100 // Or - bitwise or Or ALUOp = 0x0040 // And - bitwise and And ALUOp = 0x0050 // LSh - bitwise shift left LSh ALUOp = 0x0060 // RSh - bitwise shift right RSh ALUOp = 0x0070 // Neg - sign/unsign signing bit Neg ALUOp = 0x0080 // Mod - modulo Mod ALUOp = 0x0090 // SMod - signed modulo SMod ALUOp = Mod + 0x0100 // Xor - bitwise xor Xor ALUOp = 0x00a0 // Mov - move value from one place to another Mov ALUOp = 0x00b0 // MovSX8 - move lower 8 bits, sign extended upper bits of target MovSX8 ALUOp = Mov + 0x0100 // MovSX16 - move lower 16 bits, sign extended upper bits of target MovSX16 ALUOp = Mov + 0x0200 // MovSX32 - move lower 32 bits, sign extended upper bits of target MovSX32 ALUOp = Mov + 0x0300 // ArSh - arithmetic shift ArSh ALUOp = 0x00c0 // Swap - endian conversions Swap ALUOp = 0x00d0 ) // HostTo converts from host to another endianness. func HostTo(endian Endianness, dst Register, size Size) Instruction { var imm int64 switch size { case Half: imm = 16 case Word: imm = 32 case DWord: imm = 64 default: return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: OpCode(ALUClass).SetALUOp(Swap).SetSource(Source(endian)), Dst: dst, Constant: imm, } } // BSwap unconditionally reverses the order of bytes in a register. func BSwap(dst Register, size Size) Instruction { var imm int64 switch size { case Half: imm = 16 case Word: imm = 32 case DWord: imm = 64 default: return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: OpCode(ALU64Class).SetALUOp(Swap), Dst: dst, Constant: imm, } } // Op returns the OpCode for an ALU operation with a given source. func (op ALUOp) Op(source Source) OpCode { return OpCode(ALU64Class).SetALUOp(op).SetSource(source) } // Reg emits `dst (op) src`. func (op ALUOp) Reg(dst, src Register) Instruction { return Instruction{ OpCode: op.Op(RegSource), Dst: dst, Src: src, } } // Imm emits `dst (op) value`. func (op ALUOp) Imm(dst Register, value int32) Instruction { return Instruction{ OpCode: op.Op(ImmSource), Dst: dst, Constant: int64(value), } } // Op32 returns the OpCode for a 32-bit ALU operation with a given source. func (op ALUOp) Op32(source Source) OpCode { return OpCode(ALUClass).SetALUOp(op).SetSource(source) } // Reg32 emits `dst (op) src`, zeroing the upper 32 bit of dst. func (op ALUOp) Reg32(dst, src Register) Instruction { return Instruction{ OpCode: op.Op32(RegSource), Dst: dst, Src: src, } } // Imm32 emits `dst (op) value`, zeroing the upper 32 bit of dst. func (op ALUOp) Imm32(dst Register, value int32) Instruction { return Instruction{ OpCode: op.Op32(ImmSource), Dst: dst, Constant: int64(value), } } golang-github-cilium-ebpf-0.21.0+ds1/asm/alu_string.go000066400000000000000000000051601520243672000224570ustar00rootroot00000000000000// Code generated by "stringer -output alu_string.go -type=Source,Endianness,ALUOp"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidSource-65535] _ = x[ImmSource-0] _ = x[RegSource-8] } const ( _Source_name_0 = "ImmSource" _Source_name_1 = "RegSource" _Source_name_2 = "InvalidSource" ) func (i Source) String() string { switch { case i == 0: return _Source_name_0 case i == 8: return _Source_name_1 case i == 65535: return _Source_name_2 default: return "Source(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidEndian-255] _ = x[LE-0] _ = x[BE-8] } const ( _Endianness_name_0 = "LE" _Endianness_name_1 = "BE" _Endianness_name_2 = "InvalidEndian" ) func (i Endianness) String() string { switch { case i == 0: return _Endianness_name_0 case i == 8: return _Endianness_name_1 case i == 255: return _Endianness_name_2 default: return "Endianness(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidALUOp-65535] _ = x[Add-0] _ = x[Sub-16] _ = x[Mul-32] _ = x[Div-48] _ = x[SDiv-304] _ = x[Or-64] _ = x[And-80] _ = x[LSh-96] _ = x[RSh-112] _ = x[Neg-128] _ = x[Mod-144] _ = x[SMod-400] _ = x[Xor-160] _ = x[Mov-176] _ = x[MovSX8-432] _ = x[MovSX16-688] _ = x[MovSX32-944] _ = x[ArSh-192] _ = x[Swap-208] } const _ALUOp_name = "AddSubMulDivOrAndLShRShNegModXorMovArShSwapSDivSModMovSX8MovSX16MovSX32InvalidALUOp" var _ALUOp_map = map[ALUOp]string{ 0: _ALUOp_name[0:3], 16: _ALUOp_name[3:6], 32: _ALUOp_name[6:9], 48: _ALUOp_name[9:12], 64: _ALUOp_name[12:14], 80: _ALUOp_name[14:17], 96: _ALUOp_name[17:20], 112: _ALUOp_name[20:23], 128: _ALUOp_name[23:26], 144: _ALUOp_name[26:29], 160: _ALUOp_name[29:32], 176: _ALUOp_name[32:35], 192: _ALUOp_name[35:39], 208: _ALUOp_name[39:43], 304: _ALUOp_name[43:47], 400: _ALUOp_name[47:51], 432: _ALUOp_name[51:57], 688: _ALUOp_name[57:64], 944: _ALUOp_name[64:71], 65535: _ALUOp_name[71:83], } func (i ALUOp) String() string { if str, ok := _ALUOp_map[i]; ok { return str } return "ALUOp(" + strconv.FormatInt(int64(i), 10) + ")" } golang-github-cilium-ebpf-0.21.0+ds1/asm/doc.go000066400000000000000000000000761520243672000210560ustar00rootroot00000000000000// Package asm is an assembler for eBPF bytecode. package asm golang-github-cilium-ebpf-0.21.0+ds1/asm/dsl_test.go000066400000000000000000000025471520243672000221370ustar00rootroot00000000000000package asm import ( "testing" ) func TestDSL(t *testing.T) { testcases := []struct { name string have Instruction want Instruction }{ {"Call", FnMapLookupElem.Call(), Instruction{OpCode: 0x85, Constant: 1}}, {"Exit", Return(), Instruction{OpCode: 0x95}}, {"LoadAbs", LoadAbs(2, Byte), Instruction{OpCode: 0x30, Constant: 2}}, {"Store", StoreMem(RFP, -4, R0, Word), Instruction{ OpCode: 0x63, Dst: RFP, Src: R0, Offset: -4, }}, {"Add.Imm", Add.Imm(R1, 22), Instruction{OpCode: 0x07, Dst: R1, Constant: 22}}, {"Add.Reg", Add.Reg(R1, R2), Instruction{OpCode: 0x0f, Dst: R1, Src: R2}}, {"Add.Imm32", Add.Imm32(R1, 22), Instruction{ OpCode: 0x04, Dst: R1, Constant: 22, }}, {"JSGT.Imm", JSGT.Imm(R1, 4, "foo"), Instruction{ OpCode: 0x65, Dst: R1, Constant: 4, Offset: -1, }.WithReference("foo")}, {"JSGT.Imm32", JSGT.Imm32(R1, -2, "foo"), Instruction{ OpCode: 0x66, Dst: R1, Constant: -2, Offset: -1, }.WithReference("foo")}, {"JSLT.Reg", JSLT.Reg(R1, R2, "foo"), Instruction{ OpCode: 0xcd, Dst: R1, Src: R2, Offset: -1, }.WithReference("foo")}, {"JSLT.Reg32", JSLT.Reg32(R1, R3, "foo"), Instruction{ OpCode: 0xce, Dst: R1, Src: R3, Offset: -1, }.WithReference("foo")}, } for _, tc := range testcases { if !tc.have.equal(tc.want) { t.Errorf("%s: have %v, want %v", tc.name, tc.have, tc.want) } } } golang-github-cilium-ebpf-0.21.0+ds1/asm/func.go000066400000000000000000000012121520243672000212350ustar00rootroot00000000000000package asm import "github.com/cilium/ebpf/internal/platform" //go:generate go tool stringer -output func_string.go -type=BuiltinFunc // BuiltinFunc is a built-in eBPF function. type BuiltinFunc uint32 // BuiltinFuncForPlatform returns a platform specific function constant. // // Use this if the library doesn't provide a constant yet. func BuiltinFuncForPlatform(plat string, value uint32) (BuiltinFunc, error) { return platform.EncodeConstant[BuiltinFunc](plat, value) } // Call emits a function call. func (fn BuiltinFunc) Call() Instruction { return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(Call), Constant: int64(fn), } } golang-github-cilium-ebpf-0.21.0+ds1/asm/func_lin.go000066400000000000000000000347471520243672000221220ustar00rootroot00000000000000// Code generated by internal/cmd/genfunctions.awk; DO NOT EDIT. package asm // Code in this file is derived from Linux, available under the GPL-2.0 WITH Linux-syscall-note. import "github.com/cilium/ebpf/internal/platform" // Built-in functions (Linux). const ( FnUnspec = BuiltinFunc(platform.LinuxTag | 0) //lint:ignore SA4016 consistency FnMapLookupElem = BuiltinFunc(platform.LinuxTag | 1) FnMapUpdateElem = BuiltinFunc(platform.LinuxTag | 2) FnMapDeleteElem = BuiltinFunc(platform.LinuxTag | 3) FnProbeRead = BuiltinFunc(platform.LinuxTag | 4) FnKtimeGetNs = BuiltinFunc(platform.LinuxTag | 5) FnTracePrintk = BuiltinFunc(platform.LinuxTag | 6) FnGetPrandomU32 = BuiltinFunc(platform.LinuxTag | 7) FnGetSmpProcessorId = BuiltinFunc(platform.LinuxTag | 8) FnSkbStoreBytes = BuiltinFunc(platform.LinuxTag | 9) FnL3CsumReplace = BuiltinFunc(platform.LinuxTag | 10) FnL4CsumReplace = BuiltinFunc(platform.LinuxTag | 11) FnTailCall = BuiltinFunc(platform.LinuxTag | 12) FnCloneRedirect = BuiltinFunc(platform.LinuxTag | 13) FnGetCurrentPidTgid = BuiltinFunc(platform.LinuxTag | 14) FnGetCurrentUidGid = BuiltinFunc(platform.LinuxTag | 15) FnGetCurrentComm = BuiltinFunc(platform.LinuxTag | 16) FnGetCgroupClassid = BuiltinFunc(platform.LinuxTag | 17) FnSkbVlanPush = BuiltinFunc(platform.LinuxTag | 18) FnSkbVlanPop = BuiltinFunc(platform.LinuxTag | 19) FnSkbGetTunnelKey = BuiltinFunc(platform.LinuxTag | 20) FnSkbSetTunnelKey = BuiltinFunc(platform.LinuxTag | 21) FnPerfEventRead = BuiltinFunc(platform.LinuxTag | 22) FnRedirect = BuiltinFunc(platform.LinuxTag | 23) FnGetRouteRealm = BuiltinFunc(platform.LinuxTag | 24) FnPerfEventOutput = BuiltinFunc(platform.LinuxTag | 25) FnSkbLoadBytes = BuiltinFunc(platform.LinuxTag | 26) FnGetStackid = BuiltinFunc(platform.LinuxTag | 27) FnCsumDiff = BuiltinFunc(platform.LinuxTag | 28) FnSkbGetTunnelOpt = BuiltinFunc(platform.LinuxTag | 29) FnSkbSetTunnelOpt = BuiltinFunc(platform.LinuxTag | 30) FnSkbChangeProto = BuiltinFunc(platform.LinuxTag | 31) FnSkbChangeType = BuiltinFunc(platform.LinuxTag | 32) FnSkbUnderCgroup = BuiltinFunc(platform.LinuxTag | 33) FnGetHashRecalc = BuiltinFunc(platform.LinuxTag | 34) FnGetCurrentTask = BuiltinFunc(platform.LinuxTag | 35) FnProbeWriteUser = BuiltinFunc(platform.LinuxTag | 36) FnCurrentTaskUnderCgroup = BuiltinFunc(platform.LinuxTag | 37) FnSkbChangeTail = BuiltinFunc(platform.LinuxTag | 38) FnSkbPullData = BuiltinFunc(platform.LinuxTag | 39) FnCsumUpdate = BuiltinFunc(platform.LinuxTag | 40) FnSetHashInvalid = BuiltinFunc(platform.LinuxTag | 41) FnGetNumaNodeId = BuiltinFunc(platform.LinuxTag | 42) FnSkbChangeHead = BuiltinFunc(platform.LinuxTag | 43) FnXdpAdjustHead = BuiltinFunc(platform.LinuxTag | 44) FnProbeReadStr = BuiltinFunc(platform.LinuxTag | 45) FnGetSocketCookie = BuiltinFunc(platform.LinuxTag | 46) FnGetSocketUid = BuiltinFunc(platform.LinuxTag | 47) FnSetHash = BuiltinFunc(platform.LinuxTag | 48) FnSetsockopt = BuiltinFunc(platform.LinuxTag | 49) FnSkbAdjustRoom = BuiltinFunc(platform.LinuxTag | 50) FnRedirectMap = BuiltinFunc(platform.LinuxTag | 51) FnSkRedirectMap = BuiltinFunc(platform.LinuxTag | 52) FnSockMapUpdate = BuiltinFunc(platform.LinuxTag | 53) FnXdpAdjustMeta = BuiltinFunc(platform.LinuxTag | 54) FnPerfEventReadValue = BuiltinFunc(platform.LinuxTag | 55) FnPerfProgReadValue = BuiltinFunc(platform.LinuxTag | 56) FnGetsockopt = BuiltinFunc(platform.LinuxTag | 57) FnOverrideReturn = BuiltinFunc(platform.LinuxTag | 58) FnSockOpsCbFlagsSet = BuiltinFunc(platform.LinuxTag | 59) FnMsgRedirectMap = BuiltinFunc(platform.LinuxTag | 60) FnMsgApplyBytes = BuiltinFunc(platform.LinuxTag | 61) FnMsgCorkBytes = BuiltinFunc(platform.LinuxTag | 62) FnMsgPullData = BuiltinFunc(platform.LinuxTag | 63) FnBind = BuiltinFunc(platform.LinuxTag | 64) FnXdpAdjustTail = BuiltinFunc(platform.LinuxTag | 65) FnSkbGetXfrmState = BuiltinFunc(platform.LinuxTag | 66) FnGetStack = BuiltinFunc(platform.LinuxTag | 67) FnSkbLoadBytesRelative = BuiltinFunc(platform.LinuxTag | 68) FnFibLookup = BuiltinFunc(platform.LinuxTag | 69) FnSockHashUpdate = BuiltinFunc(platform.LinuxTag | 70) FnMsgRedirectHash = BuiltinFunc(platform.LinuxTag | 71) FnSkRedirectHash = BuiltinFunc(platform.LinuxTag | 72) FnLwtPushEncap = BuiltinFunc(platform.LinuxTag | 73) FnLwtSeg6StoreBytes = BuiltinFunc(platform.LinuxTag | 74) FnLwtSeg6AdjustSrh = BuiltinFunc(platform.LinuxTag | 75) FnLwtSeg6Action = BuiltinFunc(platform.LinuxTag | 76) FnRcRepeat = BuiltinFunc(platform.LinuxTag | 77) FnRcKeydown = BuiltinFunc(platform.LinuxTag | 78) FnSkbCgroupId = BuiltinFunc(platform.LinuxTag | 79) FnGetCurrentCgroupId = BuiltinFunc(platform.LinuxTag | 80) FnGetLocalStorage = BuiltinFunc(platform.LinuxTag | 81) FnSkSelectReuseport = BuiltinFunc(platform.LinuxTag | 82) FnSkbAncestorCgroupId = BuiltinFunc(platform.LinuxTag | 83) FnSkLookupTcp = BuiltinFunc(platform.LinuxTag | 84) FnSkLookupUdp = BuiltinFunc(platform.LinuxTag | 85) FnSkRelease = BuiltinFunc(platform.LinuxTag | 86) FnMapPushElem = BuiltinFunc(platform.LinuxTag | 87) FnMapPopElem = BuiltinFunc(platform.LinuxTag | 88) FnMapPeekElem = BuiltinFunc(platform.LinuxTag | 89) FnMsgPushData = BuiltinFunc(platform.LinuxTag | 90) FnMsgPopData = BuiltinFunc(platform.LinuxTag | 91) FnRcPointerRel = BuiltinFunc(platform.LinuxTag | 92) FnSpinLock = BuiltinFunc(platform.LinuxTag | 93) FnSpinUnlock = BuiltinFunc(platform.LinuxTag | 94) FnSkFullsock = BuiltinFunc(platform.LinuxTag | 95) FnTcpSock = BuiltinFunc(platform.LinuxTag | 96) FnSkbEcnSetCe = BuiltinFunc(platform.LinuxTag | 97) FnGetListenerSock = BuiltinFunc(platform.LinuxTag | 98) FnSkcLookupTcp = BuiltinFunc(platform.LinuxTag | 99) FnTcpCheckSyncookie = BuiltinFunc(platform.LinuxTag | 100) FnSysctlGetName = BuiltinFunc(platform.LinuxTag | 101) FnSysctlGetCurrentValue = BuiltinFunc(platform.LinuxTag | 102) FnSysctlGetNewValue = BuiltinFunc(platform.LinuxTag | 103) FnSysctlSetNewValue = BuiltinFunc(platform.LinuxTag | 104) FnStrtol = BuiltinFunc(platform.LinuxTag | 105) FnStrtoul = BuiltinFunc(platform.LinuxTag | 106) FnSkStorageGet = BuiltinFunc(platform.LinuxTag | 107) FnSkStorageDelete = BuiltinFunc(platform.LinuxTag | 108) FnSendSignal = BuiltinFunc(platform.LinuxTag | 109) FnTcpGenSyncookie = BuiltinFunc(platform.LinuxTag | 110) FnSkbOutput = BuiltinFunc(platform.LinuxTag | 111) FnProbeReadUser = BuiltinFunc(platform.LinuxTag | 112) FnProbeReadKernel = BuiltinFunc(platform.LinuxTag | 113) FnProbeReadUserStr = BuiltinFunc(platform.LinuxTag | 114) FnProbeReadKernelStr = BuiltinFunc(platform.LinuxTag | 115) FnTcpSendAck = BuiltinFunc(platform.LinuxTag | 116) FnSendSignalThread = BuiltinFunc(platform.LinuxTag | 117) FnJiffies64 = BuiltinFunc(platform.LinuxTag | 118) FnReadBranchRecords = BuiltinFunc(platform.LinuxTag | 119) FnGetNsCurrentPidTgid = BuiltinFunc(platform.LinuxTag | 120) FnXdpOutput = BuiltinFunc(platform.LinuxTag | 121) FnGetNetnsCookie = BuiltinFunc(platform.LinuxTag | 122) FnGetCurrentAncestorCgroupId = BuiltinFunc(platform.LinuxTag | 123) FnSkAssign = BuiltinFunc(platform.LinuxTag | 124) FnKtimeGetBootNs = BuiltinFunc(platform.LinuxTag | 125) FnSeqPrintf = BuiltinFunc(platform.LinuxTag | 126) FnSeqWrite = BuiltinFunc(platform.LinuxTag | 127) FnSkCgroupId = BuiltinFunc(platform.LinuxTag | 128) FnSkAncestorCgroupId = BuiltinFunc(platform.LinuxTag | 129) FnRingbufOutput = BuiltinFunc(platform.LinuxTag | 130) FnRingbufReserve = BuiltinFunc(platform.LinuxTag | 131) FnRingbufSubmit = BuiltinFunc(platform.LinuxTag | 132) FnRingbufDiscard = BuiltinFunc(platform.LinuxTag | 133) FnRingbufQuery = BuiltinFunc(platform.LinuxTag | 134) FnCsumLevel = BuiltinFunc(platform.LinuxTag | 135) FnSkcToTcp6Sock = BuiltinFunc(platform.LinuxTag | 136) FnSkcToTcpSock = BuiltinFunc(platform.LinuxTag | 137) FnSkcToTcpTimewaitSock = BuiltinFunc(platform.LinuxTag | 138) FnSkcToTcpRequestSock = BuiltinFunc(platform.LinuxTag | 139) FnSkcToUdp6Sock = BuiltinFunc(platform.LinuxTag | 140) FnGetTaskStack = BuiltinFunc(platform.LinuxTag | 141) FnLoadHdrOpt = BuiltinFunc(platform.LinuxTag | 142) FnStoreHdrOpt = BuiltinFunc(platform.LinuxTag | 143) FnReserveHdrOpt = BuiltinFunc(platform.LinuxTag | 144) FnInodeStorageGet = BuiltinFunc(platform.LinuxTag | 145) FnInodeStorageDelete = BuiltinFunc(platform.LinuxTag | 146) FnDPath = BuiltinFunc(platform.LinuxTag | 147) FnCopyFromUser = BuiltinFunc(platform.LinuxTag | 148) FnSnprintfBtf = BuiltinFunc(platform.LinuxTag | 149) FnSeqPrintfBtf = BuiltinFunc(platform.LinuxTag | 150) FnSkbCgroupClassid = BuiltinFunc(platform.LinuxTag | 151) FnRedirectNeigh = BuiltinFunc(platform.LinuxTag | 152) FnPerCpuPtr = BuiltinFunc(platform.LinuxTag | 153) FnThisCpuPtr = BuiltinFunc(platform.LinuxTag | 154) FnRedirectPeer = BuiltinFunc(platform.LinuxTag | 155) FnTaskStorageGet = BuiltinFunc(platform.LinuxTag | 156) FnTaskStorageDelete = BuiltinFunc(platform.LinuxTag | 157) FnGetCurrentTaskBtf = BuiltinFunc(platform.LinuxTag | 158) FnBprmOptsSet = BuiltinFunc(platform.LinuxTag | 159) FnKtimeGetCoarseNs = BuiltinFunc(platform.LinuxTag | 160) FnImaInodeHash = BuiltinFunc(platform.LinuxTag | 161) FnSockFromFile = BuiltinFunc(platform.LinuxTag | 162) FnCheckMtu = BuiltinFunc(platform.LinuxTag | 163) FnForEachMapElem = BuiltinFunc(platform.LinuxTag | 164) FnSnprintf = BuiltinFunc(platform.LinuxTag | 165) FnSysBpf = BuiltinFunc(platform.LinuxTag | 166) FnBtfFindByNameKind = BuiltinFunc(platform.LinuxTag | 167) FnSysClose = BuiltinFunc(platform.LinuxTag | 168) FnTimerInit = BuiltinFunc(platform.LinuxTag | 169) FnTimerSetCallback = BuiltinFunc(platform.LinuxTag | 170) FnTimerStart = BuiltinFunc(platform.LinuxTag | 171) FnTimerCancel = BuiltinFunc(platform.LinuxTag | 172) FnGetFuncIp = BuiltinFunc(platform.LinuxTag | 173) FnGetAttachCookie = BuiltinFunc(platform.LinuxTag | 174) FnTaskPtRegs = BuiltinFunc(platform.LinuxTag | 175) FnGetBranchSnapshot = BuiltinFunc(platform.LinuxTag | 176) FnTraceVprintk = BuiltinFunc(platform.LinuxTag | 177) FnSkcToUnixSock = BuiltinFunc(platform.LinuxTag | 178) FnKallsymsLookupName = BuiltinFunc(platform.LinuxTag | 179) FnFindVma = BuiltinFunc(platform.LinuxTag | 180) FnLoop = BuiltinFunc(platform.LinuxTag | 181) FnStrncmp = BuiltinFunc(platform.LinuxTag | 182) FnGetFuncArg = BuiltinFunc(platform.LinuxTag | 183) FnGetFuncRet = BuiltinFunc(platform.LinuxTag | 184) FnGetFuncArgCnt = BuiltinFunc(platform.LinuxTag | 185) FnGetRetval = BuiltinFunc(platform.LinuxTag | 186) FnSetRetval = BuiltinFunc(platform.LinuxTag | 187) FnXdpGetBuffLen = BuiltinFunc(platform.LinuxTag | 188) FnXdpLoadBytes = BuiltinFunc(platform.LinuxTag | 189) FnXdpStoreBytes = BuiltinFunc(platform.LinuxTag | 190) FnCopyFromUserTask = BuiltinFunc(platform.LinuxTag | 191) FnSkbSetTstamp = BuiltinFunc(platform.LinuxTag | 192) FnImaFileHash = BuiltinFunc(platform.LinuxTag | 193) FnKptrXchg = BuiltinFunc(platform.LinuxTag | 194) FnMapLookupPercpuElem = BuiltinFunc(platform.LinuxTag | 195) FnSkcToMptcpSock = BuiltinFunc(platform.LinuxTag | 196) FnDynptrFromMem = BuiltinFunc(platform.LinuxTag | 197) FnRingbufReserveDynptr = BuiltinFunc(platform.LinuxTag | 198) FnRingbufSubmitDynptr = BuiltinFunc(platform.LinuxTag | 199) FnRingbufDiscardDynptr = BuiltinFunc(platform.LinuxTag | 200) FnDynptrRead = BuiltinFunc(platform.LinuxTag | 201) FnDynptrWrite = BuiltinFunc(platform.LinuxTag | 202) FnDynptrData = BuiltinFunc(platform.LinuxTag | 203) FnTcpRawGenSyncookieIpv4 = BuiltinFunc(platform.LinuxTag | 204) FnTcpRawGenSyncookieIpv6 = BuiltinFunc(platform.LinuxTag | 205) FnTcpRawCheckSyncookieIpv4 = BuiltinFunc(platform.LinuxTag | 206) FnTcpRawCheckSyncookieIpv6 = BuiltinFunc(platform.LinuxTag | 207) FnKtimeGetTaiNs = BuiltinFunc(platform.LinuxTag | 208) FnUserRingbufDrain = BuiltinFunc(platform.LinuxTag | 209) FnCgrpStorageGet = BuiltinFunc(platform.LinuxTag | 210) FnCgrpStorageDelete = BuiltinFunc(platform.LinuxTag | 211) ) golang-github-cilium-ebpf-0.21.0+ds1/asm/func_string.go000066400000000000000000000317231520243672000226350ustar00rootroot00000000000000// Code generated by "stringer -output func_string.go -type=BuiltinFunc"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[FnUnspec-0] _ = x[FnMapLookupElem-1] _ = x[FnMapUpdateElem-2] _ = x[FnMapDeleteElem-3] _ = x[FnProbeRead-4] _ = x[FnKtimeGetNs-5] _ = x[FnTracePrintk-6] _ = x[FnGetPrandomU32-7] _ = x[FnGetSmpProcessorId-8] _ = x[FnSkbStoreBytes-9] _ = x[FnL3CsumReplace-10] _ = x[FnL4CsumReplace-11] _ = x[FnTailCall-12] _ = x[FnCloneRedirect-13] _ = x[FnGetCurrentPidTgid-14] _ = x[FnGetCurrentUidGid-15] _ = x[FnGetCurrentComm-16] _ = x[FnGetCgroupClassid-17] _ = x[FnSkbVlanPush-18] _ = x[FnSkbVlanPop-19] _ = x[FnSkbGetTunnelKey-20] _ = x[FnSkbSetTunnelKey-21] _ = x[FnPerfEventRead-22] _ = x[FnRedirect-23] _ = x[FnGetRouteRealm-24] _ = x[FnPerfEventOutput-25] _ = x[FnSkbLoadBytes-26] _ = x[FnGetStackid-27] _ = x[FnCsumDiff-28] _ = x[FnSkbGetTunnelOpt-29] _ = x[FnSkbSetTunnelOpt-30] _ = x[FnSkbChangeProto-31] _ = x[FnSkbChangeType-32] _ = x[FnSkbUnderCgroup-33] _ = x[FnGetHashRecalc-34] _ = x[FnGetCurrentTask-35] _ = x[FnProbeWriteUser-36] _ = x[FnCurrentTaskUnderCgroup-37] _ = x[FnSkbChangeTail-38] _ = x[FnSkbPullData-39] _ = x[FnCsumUpdate-40] _ = x[FnSetHashInvalid-41] _ = x[FnGetNumaNodeId-42] _ = x[FnSkbChangeHead-43] _ = x[FnXdpAdjustHead-44] _ = x[FnProbeReadStr-45] _ = x[FnGetSocketCookie-46] _ = x[FnGetSocketUid-47] _ = x[FnSetHash-48] _ = x[FnSetsockopt-49] _ = x[FnSkbAdjustRoom-50] _ = x[FnRedirectMap-51] _ = x[FnSkRedirectMap-52] _ = x[FnSockMapUpdate-53] _ = x[FnXdpAdjustMeta-54] _ = x[FnPerfEventReadValue-55] _ = x[FnPerfProgReadValue-56] _ = x[FnGetsockopt-57] _ = x[FnOverrideReturn-58] _ = x[FnSockOpsCbFlagsSet-59] _ = x[FnMsgRedirectMap-60] _ = x[FnMsgApplyBytes-61] _ = x[FnMsgCorkBytes-62] _ = x[FnMsgPullData-63] _ = x[FnBind-64] _ = x[FnXdpAdjustTail-65] _ = x[FnSkbGetXfrmState-66] _ = x[FnGetStack-67] _ = x[FnSkbLoadBytesRelative-68] _ = x[FnFibLookup-69] _ = x[FnSockHashUpdate-70] _ = x[FnMsgRedirectHash-71] _ = x[FnSkRedirectHash-72] _ = x[FnLwtPushEncap-73] _ = x[FnLwtSeg6StoreBytes-74] _ = x[FnLwtSeg6AdjustSrh-75] _ = x[FnLwtSeg6Action-76] _ = x[FnRcRepeat-77] _ = x[FnRcKeydown-78] _ = x[FnSkbCgroupId-79] _ = x[FnGetCurrentCgroupId-80] _ = x[FnGetLocalStorage-81] _ = x[FnSkSelectReuseport-82] _ = x[FnSkbAncestorCgroupId-83] _ = x[FnSkLookupTcp-84] _ = x[FnSkLookupUdp-85] _ = x[FnSkRelease-86] _ = x[FnMapPushElem-87] _ = x[FnMapPopElem-88] _ = x[FnMapPeekElem-89] _ = x[FnMsgPushData-90] _ = x[FnMsgPopData-91] _ = x[FnRcPointerRel-92] _ = x[FnSpinLock-93] _ = x[FnSpinUnlock-94] _ = x[FnSkFullsock-95] _ = x[FnTcpSock-96] _ = x[FnSkbEcnSetCe-97] _ = x[FnGetListenerSock-98] _ = x[FnSkcLookupTcp-99] _ = x[FnTcpCheckSyncookie-100] _ = x[FnSysctlGetName-101] _ = x[FnSysctlGetCurrentValue-102] _ = x[FnSysctlGetNewValue-103] _ = x[FnSysctlSetNewValue-104] _ = x[FnStrtol-105] _ = x[FnStrtoul-106] _ = x[FnSkStorageGet-107] _ = x[FnSkStorageDelete-108] _ = x[FnSendSignal-109] _ = x[FnTcpGenSyncookie-110] _ = x[FnSkbOutput-111] _ = x[FnProbeReadUser-112] _ = x[FnProbeReadKernel-113] _ = x[FnProbeReadUserStr-114] _ = x[FnProbeReadKernelStr-115] _ = x[FnTcpSendAck-116] _ = x[FnSendSignalThread-117] _ = x[FnJiffies64-118] _ = x[FnReadBranchRecords-119] _ = x[FnGetNsCurrentPidTgid-120] _ = x[FnXdpOutput-121] _ = x[FnGetNetnsCookie-122] _ = x[FnGetCurrentAncestorCgroupId-123] _ = x[FnSkAssign-124] _ = x[FnKtimeGetBootNs-125] _ = x[FnSeqPrintf-126] _ = x[FnSeqWrite-127] _ = x[FnSkCgroupId-128] _ = x[FnSkAncestorCgroupId-129] _ = x[FnRingbufOutput-130] _ = x[FnRingbufReserve-131] _ = x[FnRingbufSubmit-132] _ = x[FnRingbufDiscard-133] _ = x[FnRingbufQuery-134] _ = x[FnCsumLevel-135] _ = x[FnSkcToTcp6Sock-136] _ = x[FnSkcToTcpSock-137] _ = x[FnSkcToTcpTimewaitSock-138] _ = x[FnSkcToTcpRequestSock-139] _ = x[FnSkcToUdp6Sock-140] _ = x[FnGetTaskStack-141] _ = x[FnLoadHdrOpt-142] _ = x[FnStoreHdrOpt-143] _ = x[FnReserveHdrOpt-144] _ = x[FnInodeStorageGet-145] _ = x[FnInodeStorageDelete-146] _ = x[FnDPath-147] _ = x[FnCopyFromUser-148] _ = x[FnSnprintfBtf-149] _ = x[FnSeqPrintfBtf-150] _ = x[FnSkbCgroupClassid-151] _ = x[FnRedirectNeigh-152] _ = x[FnPerCpuPtr-153] _ = x[FnThisCpuPtr-154] _ = x[FnRedirectPeer-155] _ = x[FnTaskStorageGet-156] _ = x[FnTaskStorageDelete-157] _ = x[FnGetCurrentTaskBtf-158] _ = x[FnBprmOptsSet-159] _ = x[FnKtimeGetCoarseNs-160] _ = x[FnImaInodeHash-161] _ = x[FnSockFromFile-162] _ = x[FnCheckMtu-163] _ = x[FnForEachMapElem-164] _ = x[FnSnprintf-165] _ = x[FnSysBpf-166] _ = x[FnBtfFindByNameKind-167] _ = x[FnSysClose-168] _ = x[FnTimerInit-169] _ = x[FnTimerSetCallback-170] _ = x[FnTimerStart-171] _ = x[FnTimerCancel-172] _ = x[FnGetFuncIp-173] _ = x[FnGetAttachCookie-174] _ = x[FnTaskPtRegs-175] _ = x[FnGetBranchSnapshot-176] _ = x[FnTraceVprintk-177] _ = x[FnSkcToUnixSock-178] _ = x[FnKallsymsLookupName-179] _ = x[FnFindVma-180] _ = x[FnLoop-181] _ = x[FnStrncmp-182] _ = x[FnGetFuncArg-183] _ = x[FnGetFuncRet-184] _ = x[FnGetFuncArgCnt-185] _ = x[FnGetRetval-186] _ = x[FnSetRetval-187] _ = x[FnXdpGetBuffLen-188] _ = x[FnXdpLoadBytes-189] _ = x[FnXdpStoreBytes-190] _ = x[FnCopyFromUserTask-191] _ = x[FnSkbSetTstamp-192] _ = x[FnImaFileHash-193] _ = x[FnKptrXchg-194] _ = x[FnMapLookupPercpuElem-195] _ = x[FnSkcToMptcpSock-196] _ = x[FnDynptrFromMem-197] _ = x[FnRingbufReserveDynptr-198] _ = x[FnRingbufSubmitDynptr-199] _ = x[FnRingbufDiscardDynptr-200] _ = x[FnDynptrRead-201] _ = x[FnDynptrWrite-202] _ = x[FnDynptrData-203] _ = x[FnTcpRawGenSyncookieIpv4-204] _ = x[FnTcpRawGenSyncookieIpv6-205] _ = x[FnTcpRawCheckSyncookieIpv4-206] _ = x[FnTcpRawCheckSyncookieIpv6-207] _ = x[FnKtimeGetTaiNs-208] _ = x[FnUserRingbufDrain-209] _ = x[FnCgrpStorageGet-210] _ = x[FnCgrpStorageDelete-211] _ = x[WindowsFnMapLookupElem-268435457] _ = x[WindowsFnMapUpdateElem-268435458] _ = x[WindowsFnMapDeleteElem-268435459] _ = x[WindowsFnMapLookupAndDeleteElem-268435460] _ = x[WindowsFnTailCall-268435461] _ = x[WindowsFnGetPrandomU32-268435462] _ = x[WindowsFnKtimeGetBootNs-268435463] _ = x[WindowsFnGetSmpProcessorId-268435464] _ = x[WindowsFnKtimeGetNs-268435465] _ = x[WindowsFnCsumDiff-268435466] _ = x[WindowsFnRingbufOutput-268435467] _ = x[WindowsFnTracePrintk2-268435468] _ = x[WindowsFnTracePrintk3-268435469] _ = x[WindowsFnTracePrintk4-268435470] _ = x[WindowsFnTracePrintk5-268435471] _ = x[WindowsFnMapPushElem-268435472] _ = x[WindowsFnMapPopElem-268435473] _ = x[WindowsFnMapPeekElem-268435474] _ = x[WindowsFnGetCurrentPidTgid-268435475] _ = x[WindowsFnGetCurrentLogonId-268435476] _ = x[WindowsFnIsCurrentAdmin-268435477] _ = x[WindowsFnMemcpyS-268435478] _ = x[WindowsFnMemcmpS-268435479] _ = x[WindowsFnMemset-268435480] _ = x[WindowsFnMemmoveS-268435481] _ = x[WindowsFnGetSocketCookie-268435482] _ = x[WindowsFnStrncpyS-268435483] _ = x[WindowsFnStrncatS-268435484] _ = x[WindowsFnStrnlenS-268435485] _ = x[WindowsFnKtimeGetBootMs-268435486] _ = x[WindowsFnKtimeGetMs-268435487] _ = x[WindowsFnPerfEventOutput-268435488] _ = x[WindowsFnGetCurrentProcessStartKey-268435489] _ = x[WindowsFnGetCurrentThreadCreateTime-268435490] } const ( _BuiltinFunc_name_0 = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDelete" _BuiltinFunc_name_1 = "WindowsFnMapLookupElemWindowsFnMapUpdateElemWindowsFnMapDeleteElemWindowsFnMapLookupAndDeleteElemWindowsFnTailCallWindowsFnGetPrandomU32WindowsFnKtimeGetBootNsWindowsFnGetSmpProcessorIdWindowsFnKtimeGetNsWindowsFnCsumDiffWindowsFnRingbufOutputWindowsFnTracePrintk2WindowsFnTracePrintk3WindowsFnTracePrintk4WindowsFnTracePrintk5WindowsFnMapPushElemWindowsFnMapPopElemWindowsFnMapPeekElemWindowsFnGetCurrentPidTgidWindowsFnGetCurrentLogonIdWindowsFnIsCurrentAdminWindowsFnMemcpySWindowsFnMemcmpSWindowsFnMemsetWindowsFnMemmoveSWindowsFnGetSocketCookieWindowsFnStrncpySWindowsFnStrncatSWindowsFnStrnlenSWindowsFnKtimeGetBootMsWindowsFnKtimeGetMsWindowsFnPerfEventOutputWindowsFnGetCurrentProcessStartKeyWindowsFnGetCurrentThreadCreateTime" ) var ( _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} _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} ) func (i BuiltinFunc) String() string { switch { case i <= 211: return _BuiltinFunc_name_0[_BuiltinFunc_index_0[i]:_BuiltinFunc_index_0[i+1]] case 268435457 <= i && i <= 268435490: i -= 268435457 return _BuiltinFunc_name_1[_BuiltinFunc_index_1[i]:_BuiltinFunc_index_1[i+1]] default: return "BuiltinFunc(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-cilium-ebpf-0.21.0+ds1/asm/func_win.go000066400000000000000000000054721520243672000221260ustar00rootroot00000000000000// Code generated by internal/cmd/genwinfunctions.awk; DO NOT EDIT. package asm // Code in this file is derived from eBPF for Windows, available under the MIT License. import "github.com/cilium/ebpf/internal/platform" // Built-in functions (Windows). const ( WindowsFnMapLookupElem = BuiltinFunc(platform.WindowsTag | 1) WindowsFnMapUpdateElem = BuiltinFunc(platform.WindowsTag | 2) WindowsFnMapDeleteElem = BuiltinFunc(platform.WindowsTag | 3) WindowsFnMapLookupAndDeleteElem = BuiltinFunc(platform.WindowsTag | 4) WindowsFnTailCall = BuiltinFunc(platform.WindowsTag | 5) WindowsFnGetPrandomU32 = BuiltinFunc(platform.WindowsTag | 6) WindowsFnKtimeGetBootNs = BuiltinFunc(platform.WindowsTag | 7) WindowsFnGetSmpProcessorId = BuiltinFunc(platform.WindowsTag | 8) WindowsFnKtimeGetNs = BuiltinFunc(platform.WindowsTag | 9) WindowsFnCsumDiff = BuiltinFunc(platform.WindowsTag | 10) WindowsFnRingbufOutput = BuiltinFunc(platform.WindowsTag | 11) WindowsFnTracePrintk2 = BuiltinFunc(platform.WindowsTag | 12) WindowsFnTracePrintk3 = BuiltinFunc(platform.WindowsTag | 13) WindowsFnTracePrintk4 = BuiltinFunc(platform.WindowsTag | 14) WindowsFnTracePrintk5 = BuiltinFunc(platform.WindowsTag | 15) WindowsFnMapPushElem = BuiltinFunc(platform.WindowsTag | 16) WindowsFnMapPopElem = BuiltinFunc(platform.WindowsTag | 17) WindowsFnMapPeekElem = BuiltinFunc(platform.WindowsTag | 18) WindowsFnGetCurrentPidTgid = BuiltinFunc(platform.WindowsTag | 19) WindowsFnGetCurrentLogonId = BuiltinFunc(platform.WindowsTag | 20) WindowsFnIsCurrentAdmin = BuiltinFunc(platform.WindowsTag | 21) WindowsFnMemcpyS = BuiltinFunc(platform.WindowsTag | 22) WindowsFnMemcmpS = BuiltinFunc(platform.WindowsTag | 23) WindowsFnMemset = BuiltinFunc(platform.WindowsTag | 24) WindowsFnMemmoveS = BuiltinFunc(platform.WindowsTag | 25) WindowsFnGetSocketCookie = BuiltinFunc(platform.WindowsTag | 26) WindowsFnStrncpyS = BuiltinFunc(platform.WindowsTag | 27) WindowsFnStrncatS = BuiltinFunc(platform.WindowsTag | 28) WindowsFnStrnlenS = BuiltinFunc(platform.WindowsTag | 29) WindowsFnKtimeGetBootMs = BuiltinFunc(platform.WindowsTag | 30) WindowsFnKtimeGetMs = BuiltinFunc(platform.WindowsTag | 31) WindowsFnPerfEventOutput = BuiltinFunc(platform.WindowsTag | 32) WindowsFnGetCurrentProcessStartKey = BuiltinFunc(platform.WindowsTag | 33) WindowsFnGetCurrentThreadCreateTime = BuiltinFunc(platform.WindowsTag | 34) ) golang-github-cilium-ebpf-0.21.0+ds1/asm/instruction.go000066400000000000000000000630741520243672000227010ustar00rootroot00000000000000package asm import ( "crypto/sha1" "crypto/sha256" "encoding/binary" "encoding/hex" "errors" "fmt" "hash" "io" "math" "sort" "strings" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" ) // InstructionSize is the size of a BPF instruction in bytes const InstructionSize = 8 // RawInstructionOffset is an offset in units of raw BPF instructions. type RawInstructionOffset uint64 var ErrUnreferencedSymbol = errors.New("unreferenced symbol") var ErrUnsatisfiedMapReference = errors.New("unsatisfied map reference") var ErrUnsatisfiedProgramReference = errors.New("unsatisfied program reference") // Bytes returns the offset of an instruction in bytes. func (rio RawInstructionOffset) Bytes() uint64 { return uint64(rio) * InstructionSize } // Instruction is a single eBPF instruction. type Instruction struct { OpCode OpCode Dst Register Src Register Offset int16 Constant int64 // Metadata contains optional metadata about this instruction. Metadata Metadata } // Width returns how many raw BPF instructions the Instruction occupies within // an instruction stream. For example, an Instruction encoding a 64-bit value // will typically occupy 2 raw instructions, while a 32-bit constant can be // encoded in a single raw instruction. func (ins *Instruction) Width() RawInstructionOffset { return RawInstructionOffset(ins.OpCode.rawInstructions()) } // Unmarshal decodes a BPF instruction. func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder, platform string) error { data := make([]byte, InstructionSize) if _, err := io.ReadFull(r, data); err != nil { return err } ins.OpCode = OpCode(data[0]) regs := data[1] switch bo { case binary.LittleEndian: ins.Dst, ins.Src = Register(regs&0xF), Register(regs>>4) case binary.BigEndian: ins.Dst, ins.Src = Register(regs>>4), Register(regs&0xf) } ins.Offset = int16(bo.Uint16(data[2:4])) // Convert to int32 before widening to int64 // to ensure the signed bit is carried over. ins.Constant = int64(int32(bo.Uint32(data[4:8]))) if ins.IsBuiltinCall() { if ins.Constant >= 0 { // Leave negative constants from the instruction stream // unchanged. These are sometimes used as placeholders for later // patching. // This relies on not having a valid platform tag with a high bit set. fn, err := BuiltinFuncForPlatform(platform, uint32(ins.Constant)) if err != nil { return err } ins.Constant = int64(fn) } } else if ins.OpCode.Class().IsALU() { switch ins.OpCode.ALUOp() { case Div: if ins.Offset == 1 { ins.OpCode = ins.OpCode.SetALUOp(SDiv) ins.Offset = 0 } case Mod: if ins.Offset == 1 { ins.OpCode = ins.OpCode.SetALUOp(SMod) ins.Offset = 0 } case Mov: switch ins.Offset { case 8: ins.OpCode = ins.OpCode.SetALUOp(MovSX8) ins.Offset = 0 case 16: ins.OpCode = ins.OpCode.SetALUOp(MovSX16) ins.Offset = 0 case 32: ins.OpCode = ins.OpCode.SetALUOp(MovSX32) ins.Offset = 0 } } } else if ins.OpCode.Class() == StXClass && ins.OpCode.Mode() == AtomicMode { // For atomic ops, part of the opcode is stored in the // constant field. Shift over 8 bytes so we can OR with the actual opcode and // apply `atomicMask` to avoid merging unknown bits that may be added in the future. ins.OpCode |= (OpCode((ins.Constant << 8)) & atomicMask) } if !ins.OpCode.IsDWordLoad() { return nil } // Pull another instruction from the stream to retrieve the second // half of the 64-bit immediate value. if _, err := io.ReadFull(r, data); err != nil { // No Wrap, to avoid io.EOF clash return errors.New("64bit immediate is missing second half") } // Require that all fields other than the value are zero. if bo.Uint32(data[0:4]) != 0 { return errors.New("64bit immediate has non-zero fields") } cons1 := uint32(ins.Constant) cons2 := int32(bo.Uint32(data[4:8])) ins.Constant = int64(cons2)<<32 | int64(cons1) return nil } // Marshal encodes a BPF instruction. func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) { if ins.OpCode == InvalidOpCode { return 0, errors.New("invalid opcode") } isDWordLoad := ins.OpCode.IsDWordLoad() cons := int32(ins.Constant) if isDWordLoad { // Encode least significant 32bit first for 64bit operations. cons = int32(uint32(ins.Constant)) } regs, err := newBPFRegisters(ins.Dst, ins.Src, bo) if err != nil { return 0, fmt.Errorf("can't marshal registers: %s", err) } if ins.IsBuiltinCall() { fn := BuiltinFunc(ins.Constant) plat, value := platform.DecodeConstant(fn) if plat != platform.Native { return 0, fmt.Errorf("function %s (%s): %w", fn, plat, internal.ErrNotSupportedOnOS) } cons = int32(value) } else if ins.OpCode.Class().IsALU() { newOffset := int16(0) switch ins.OpCode.ALUOp() { case SDiv: ins.OpCode = ins.OpCode.SetALUOp(Div) newOffset = 1 case SMod: ins.OpCode = ins.OpCode.SetALUOp(Mod) newOffset = 1 case MovSX8: ins.OpCode = ins.OpCode.SetALUOp(Mov) newOffset = 8 case MovSX16: ins.OpCode = ins.OpCode.SetALUOp(Mov) newOffset = 16 case MovSX32: ins.OpCode = ins.OpCode.SetALUOp(Mov) newOffset = 32 } if newOffset != 0 && ins.Offset != 0 { return 0, fmt.Errorf("extended ALU opcodes should have an .Offset of 0: %s", ins) } ins.Offset = newOffset } else if atomic := ins.OpCode.AtomicOp(); atomic != InvalidAtomic { ins.OpCode = ins.OpCode &^ atomicMask ins.Constant = int64(atomic >> 8) } op, err := ins.OpCode.bpfOpCode() if err != nil { return 0, err } data := make([]byte, InstructionSize) data[0] = op data[1] = byte(regs) bo.PutUint16(data[2:4], uint16(ins.Offset)) bo.PutUint32(data[4:8], uint32(cons)) if _, err := w.Write(data); err != nil { return 0, err } if !isDWordLoad { return InstructionSize, nil } // The first half of the second part of a double-wide instruction // must be zero. The second half carries the value. bo.PutUint32(data[0:4], 0) bo.PutUint32(data[4:8], uint32(ins.Constant>>32)) if _, err := w.Write(data); err != nil { return 0, err } return 2 * InstructionSize, nil } // AssociateMap associates a Map with this Instruction. // // Implicitly clears the Instruction's Reference field. // // Returns an error if the Instruction is not a map load. func (ins *Instruction) AssociateMap(m FDer) error { if !ins.IsLoadFromMap() { return errors.New("not a load from a map") } ins.Metadata.Set(referenceMeta{}, nil) ins.Metadata.Set(mapMeta{}, m) return nil } func (ins *Instruction) encodeMapFD(fd int) { // Preserve the offset value for direct map loads. offset := uint64(ins.Constant) & (math.MaxUint32 << 32) rawFd := uint64(uint32(fd)) ins.Constant = int64(offset | rawFd) } // mapFd returns the map file descriptor stored in the 32 least significant // bits of ins' Constant field. func (ins *Instruction) mapFd() int { return int(int32(ins.Constant)) } // RewriteMapOffset changes the offset of a direct load from a map. // // Returns an error if the instruction is not a direct load. func (ins *Instruction) RewriteMapOffset(offset uint32) error { if !ins.OpCode.IsDWordLoad() { return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) } if ins.Src != PseudoMapValue { return errors.New("not a direct load from a map") } fd := uint64(ins.Constant) & math.MaxUint32 ins.Constant = int64(uint64(offset)<<32 | fd) return nil } func (ins *Instruction) mapOffset() uint32 { return uint32(uint64(ins.Constant) >> 32) } // IsLoadFromMap returns true if the instruction loads from a map. // // This covers both loading the map pointer and direct map value loads. func (ins *Instruction) IsLoadFromMap() bool { return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue) } // IsFunctionCall returns true if the instruction calls another BPF function. // // This is not the same thing as a BPF helper call. func (ins *Instruction) IsFunctionCall() bool { return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall } // IsKfuncCall returns true if the instruction calls a kfunc. // // This is not the same thing as a BPF helper call. func (ins *Instruction) IsKfuncCall() bool { return ins.OpCode.JumpOp() == Call && ins.Src == PseudoKfuncCall } // IsLoadOfFunctionPointer returns true if the instruction loads a function pointer. func (ins *Instruction) IsLoadOfFunctionPointer() bool { return ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc } // IsFunctionReference returns true if the instruction references another BPF // function, either by invoking a Call jump operation or by loading a function // pointer. func (ins *Instruction) IsFunctionReference() bool { return ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer() } // IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call. func (ins *Instruction) IsBuiltinCall() bool { return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0 } // IsConstantLoad returns true if the instruction loads a constant of the // given size. func (ins *Instruction) IsConstantLoad(size Size) bool { return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0 } // Format implements fmt.Formatter. func (ins Instruction) Format(f fmt.State, c rune) { if c != 'v' { fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c) return } op := ins.OpCode if op == InvalidOpCode { fmt.Fprint(f, "INVALID") return } // Omit trailing space for Exit if op.JumpOp() == Exit { fmt.Fprint(f, op) return } if ins.IsLoadFromMap() { fd := ins.mapFd() m := ins.Map() switch ins.Src { case PseudoMapFD: if m != nil { fmt.Fprintf(f, "LoadMapPtr dst: %s map: %s", ins.Dst, m) } else { fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd) } case PseudoMapValue: if m != nil { fmt.Fprintf(f, "LoadMapValue dst: %s, map: %s off: %d", ins.Dst, m, ins.mapOffset()) } else { fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset()) } } goto ref } switch cls := op.Class(); { case cls.isLoadOrStore(): fmt.Fprintf(f, "%v ", op) switch op.Mode() { case ImmMode: fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant) case AbsMode: fmt.Fprintf(f, "imm: %d", ins.Constant) case IndMode: fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant) case MemMode, MemSXMode: fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant) case AtomicMode: fmt.Fprintf(f, "dst: %s src: %s off: %d", ins.Dst, ins.Src, ins.Offset) } case cls.IsALU(): fmt.Fprintf(f, "%v", op) if op == Swap.Op(ImmSource) { fmt.Fprintf(f, "%d", ins.Constant) } fmt.Fprintf(f, " dst: %s ", ins.Dst) switch { case op.ALUOp() == Swap: break case op.Source() == ImmSource: fmt.Fprintf(f, "imm: %d", ins.Constant) default: fmt.Fprintf(f, "src: %s", ins.Src) } case cls.IsJump(): fmt.Fprintf(f, "%v ", op) switch jop := op.JumpOp(); jop { case Call: switch ins.Src { case PseudoCall: // bpf-to-bpf call fmt.Fprint(f, ins.Constant) case PseudoKfuncCall: // kfunc call fmt.Fprintf(f, "Kfunc(%d)", ins.Constant) default: fmt.Fprint(f, BuiltinFunc(ins.Constant)) } case Ja: if ins.OpCode.Class() == Jump32Class { fmt.Fprintf(f, "imm: %d", ins.Constant) } else { fmt.Fprintf(f, "off: %d", ins.Offset) } default: fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset) if op.Source() == ImmSource { fmt.Fprintf(f, "imm: %d", ins.Constant) } else { fmt.Fprintf(f, "src: %s", ins.Src) } } default: fmt.Fprintf(f, "%v ", op) } ref: if ins.Reference() != "" { fmt.Fprintf(f, " <%s>", ins.Reference()) } } func (ins Instruction) equal(other Instruction) bool { return ins.OpCode == other.OpCode && ins.Dst == other.Dst && ins.Src == other.Src && ins.Offset == other.Offset && ins.Constant == other.Constant } // Size returns the amount of bytes ins would occupy in binary form. func (ins Instruction) Size() uint64 { return uint64(InstructionSize * ins.OpCode.rawInstructions()) } // WithMetadata sets the given Metadata on the Instruction. e.g. to copy // Metadata from another Instruction when replacing it. func (ins Instruction) WithMetadata(meta Metadata) Instruction { ins.Metadata = meta return ins } type symbolMeta struct{} // WithSymbol marks the Instruction as a Symbol, which other Instructions // can point to using corresponding calls to WithReference. func (ins Instruction) WithSymbol(name string) Instruction { ins.Metadata.Set(symbolMeta{}, name) return ins } // Symbol returns the value ins has been marked with using WithSymbol, // otherwise returns an empty string. A symbol is often an Instruction // at the start of a function body. func (ins Instruction) Symbol() string { sym, _ := ins.Metadata.Get(symbolMeta{}).(string) return sym } type referenceMeta struct{} // WithReference makes ins reference another Symbol or map by name. func (ins Instruction) WithReference(ref string) Instruction { ins.Metadata.Set(referenceMeta{}, ref) return ins } // Reference returns the Symbol or map name referenced by ins, if any. func (ins Instruction) Reference() string { ref, _ := ins.Metadata.Get(referenceMeta{}).(string) return ref } type mapMeta struct{} // Map returns the Map referenced by ins, if any. // An Instruction will contain a Map if e.g. it references an existing, // pinned map that was opened during ELF loading. func (ins Instruction) Map() FDer { fd, _ := ins.Metadata.Get(mapMeta{}).(FDer) return fd } type sourceMeta struct{} // WithSource adds source information about the Instruction. func (ins Instruction) WithSource(src fmt.Stringer) Instruction { ins.Metadata.Set(sourceMeta{}, src) return ins } // Source returns source information about the Instruction. The field is // present when the compiler emits BTF line info about the Instruction and // usually contains the line of source code responsible for it. func (ins Instruction) Source() fmt.Stringer { str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer) return str } // A Comment can be passed to Instruction.WithSource to add a comment // to an instruction. type Comment string func (s Comment) String() string { return string(s) } // FDer represents a resource tied to an underlying file descriptor. // Used as a stand-in for e.g. ebpf.Map since that type cannot be // imported here and FD() is the only method we rely on. type FDer interface { FD() int } // Instructions is an eBPF program. type Instructions []Instruction // AppendInstructions decodes [Instruction] from r and appends them to insns. func AppendInstructions(insns Instructions, r io.Reader, bo binary.ByteOrder, platform string) (Instructions, error) { var offset uint64 for { var ins Instruction err := ins.Unmarshal(r, bo, platform) if errors.Is(err, io.EOF) { break } if err != nil { return nil, fmt.Errorf("offset %d: %w", offset, err) } insns = append(insns, ins) offset += ins.Size() } return insns, nil } // Name returns the name of the function insns belongs to, if any. func (insns Instructions) Name() string { if len(insns) == 0 { return "" } return insns[0].Symbol() } func (insns Instructions) String() string { return fmt.Sprint(insns) } // Size returns the amount of bytes insns would occupy in binary form. func (insns Instructions) Size() uint64 { var sum uint64 for _, ins := range insns { sum += ins.Size() } return sum } // AssociateMap updates all Instructions that Reference the given symbol // to point to an existing Map m instead. // // Returns ErrUnreferencedSymbol error if no references to symbol are found // in insns. If symbol is anything else than the symbol name of map (e.g. // a bpf2bpf subprogram), an error is returned. func (insns Instructions) AssociateMap(symbol string, m FDer) error { if symbol == "" { return errors.New("empty symbol") } var found bool for i := range insns { ins := &insns[i] if ins.Reference() != symbol { continue } if err := ins.AssociateMap(m); err != nil { return err } found = true } if !found { return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol) } return nil } // SymbolOffsets returns the set of symbols and their offset in // the instructions. func (insns Instructions) SymbolOffsets() (map[string]int, error) { offsets := make(map[string]int) for i, ins := range insns { if ins.Symbol() == "" { continue } if _, ok := offsets[ins.Symbol()]; ok { return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol()) } offsets[ins.Symbol()] = i } return offsets, nil } // FunctionReferences returns a set of symbol names these Instructions make // bpf-to-bpf calls to. func (insns Instructions) FunctionReferences() []string { calls := make(map[string]struct{}) for _, ins := range insns { if ins.Constant != -1 { // BPF-to-BPF calls have -1 constants. continue } if ins.Reference() == "" { continue } if !ins.IsFunctionReference() { continue } calls[ins.Reference()] = struct{}{} } result := make([]string, 0, len(calls)) for call := range calls { result = append(result, call) } sort.Strings(result) return result } // ReferenceOffsets returns the set of references and their offset in // the instructions. func (insns Instructions) ReferenceOffsets() map[string][]int { offsets := make(map[string][]int) for i, ins := range insns { if ins.Reference() == "" { continue } offsets[ins.Reference()] = append(offsets[ins.Reference()], i) } return offsets } // Format implements fmt.Formatter. // // You can control indentation of symbols by // specifying a width. Setting a precision controls the indentation of // instructions. // The default character is a tab, which can be overridden by specifying // the ' ' space flag. func (insns Instructions) Format(f fmt.State, c rune) { if c != 's' && c != 'v' { fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c) return } // Precision is better in this case, because it allows // specifying 0 padding easily. padding, ok := f.Precision() if !ok { padding = 1 } indent := strings.Repeat("\t", padding) if f.Flag(' ') { indent = strings.Repeat(" ", padding) } symPadding, ok := f.Width() if !ok { symPadding = padding - 1 } if symPadding < 0 { symPadding = 0 } symIndent := strings.Repeat("\t", symPadding) if f.Flag(' ') { symIndent = strings.Repeat(" ", symPadding) } // Guess how many digits we need at most, by assuming that all instructions // are double wide. highestOffset := len(insns) * 2 offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset)))) iter := insns.Iterate() for iter.Next() { if iter.Ins.Symbol() != "" { fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol()) } if src := iter.Ins.Source(); src != nil { line := strings.TrimSpace(src.String()) if line != "" { fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line) } } fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins) } } // Marshal encodes a BPF program into the kernel format. // // insns may be modified if there are unresolved jumps or bpf2bpf calls. // // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction // without a matching Symbol Instruction within insns. func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { if err := insns.encodeFunctionReferences(); err != nil { return err } if err := insns.encodeMapPointers(); err != nil { return err } for i, ins := range insns { if _, err := ins.Marshal(w, bo); err != nil { return fmt.Errorf("instruction %d: %w", i, err) } } return nil } // Tag calculates the kernel tag for a series of instructions. // // It mirrors bpf_prog_calc_tag in the kernel and so can be compared // to ProgramInfo.Tag to figure out whether a loaded program matches // certain instructions. // // Deprecated: The value produced by this method no longer matches tags produced // by the kernel since Linux 6.18. Use [Instructions.HasTag] instead. func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) { // We cannot determine which hashing function to use without probing the kernel. // So use the legacy SHA-1 implementation and deprecate this method. return insns.tagSha1(bo) } // HasTag returns true if the given tag matches the kernel tag of insns. func (insns Instructions) HasTag(tag string, bo binary.ByteOrder) (bool, error) { sha256Tag, err := insns.tagSha256(bo) if err != nil { return false, fmt.Errorf("hashing sha256: %w", err) } if tag == sha256Tag { return true, nil } sha1Tag, err := insns.tagSha1(bo) if err != nil { return false, fmt.Errorf("hashing sha1: %w", err) } return tag == sha1Tag, nil } // tagSha1 calculates the kernel tag for a series of instructions. // // It mirrors bpf_prog_calc_tag in kernels up to v6.18 and can be compared to // ProgramInfo.Tag to figure out whether a loaded Program matches insns. func (insns Instructions) tagSha1(bo binary.ByteOrder) (string, error) { h := sha1.New() if err := insns.hash(h, bo); err != nil { return "", err } return hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil } // tagSha256 calculates the kernel tag for a series of instructions. // // It mirrors bpf_prog_calc_tag in the kernel and can be compared to // ProgramInfo.Tag to figure out whether a loaded Program matches insns. func (insns Instructions) tagSha256(bo binary.ByteOrder) (string, error) { h := sha256.New() if err := insns.hash(h, bo); err != nil { return "", err } return hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil } // hash calculates the hash of the instruction stream. Map load instructions // are zeroed out, since these contain map file descriptors or pointers to // maps, which will be different from load to load and would make the hash // non-deterministic. func (insns Instructions) hash(h hash.Hash, bo binary.ByteOrder) error { for i, ins := range insns { if ins.IsLoadFromMap() { ins.Constant = 0 } _, err := ins.Marshal(h, bo) if err != nil { return fmt.Errorf("instruction %d: %w", i, err) } } return nil } // encodeFunctionReferences populates the Offset (or Constant, depending on // the instruction type) field of instructions with a Reference field to point // to the offset of the corresponding instruction with a matching Symbol field. // // Only Reference Instructions that are either jumps or BPF function references // (calls or function pointer loads) are populated. // // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction // without at least one corresponding Symbol Instruction within insns. func (insns Instructions) encodeFunctionReferences() error { // Index the offsets of instructions tagged as a symbol. symbolOffsets := make(map[string]RawInstructionOffset) iter := insns.Iterate() for iter.Next() { ins := iter.Ins if ins.Symbol() == "" { continue } if _, ok := symbolOffsets[ins.Symbol()]; ok { return fmt.Errorf("duplicate symbol %s", ins.Symbol()) } symbolOffsets[ins.Symbol()] = iter.Offset } // Find all instructions tagged as references to other symbols. // Depending on the instruction type, populate their constant or offset // fields to point to the symbol they refer to within the insn stream. iter = insns.Iterate() for iter.Next() { i := iter.Index offset := iter.Offset ins := iter.Ins if ins.Reference() == "" { continue } switch { case ins.IsFunctionReference() && ins.Constant == -1, ins.OpCode == Ja.opCode(Jump32Class, ImmSource) && ins.Constant == -1: symOffset, ok := symbolOffsets[ins.Reference()] if !ok { return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) } ins.Constant = int64(symOffset - offset - 1) case ins.OpCode.Class().IsJump() && ins.Offset == -1: symOffset, ok := symbolOffsets[ins.Reference()] if !ok { return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) } ins.Offset = int16(symOffset - offset - 1) } } return nil } // encodeMapPointers finds all Map Instructions and encodes their FDs // into their Constant fields. func (insns Instructions) encodeMapPointers() error { iter := insns.Iterate() for iter.Next() { ins := iter.Ins if !ins.IsLoadFromMap() { continue } m := ins.Map() if m == nil { continue } fd := m.FD() if fd < 0 { return fmt.Errorf("map %s: %w", m, sys.ErrClosedFd) } ins.encodeMapFD(m.FD()) } return nil } // Iterate allows iterating a BPF program while keeping track of // various offsets. // // Modifying the instruction slice will lead to undefined behaviour. func (insns Instructions) Iterate() *InstructionIterator { return &InstructionIterator{insns: insns} } // InstructionIterator iterates over a BPF program. type InstructionIterator struct { insns Instructions // The instruction in question. Ins *Instruction // The index of the instruction in the original instruction slice. Index int // The offset of the instruction in raw BPF instructions. This accounts // for double-wide instructions. Offset RawInstructionOffset } // Next returns true as long as there are any instructions remaining. func (iter *InstructionIterator) Next() bool { if len(iter.insns) == 0 { return false } if iter.Ins != nil { iter.Index++ iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions()) } iter.Ins = &iter.insns[0] iter.insns = iter.insns[1:] return true } type bpfRegisters uint8 func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) { switch bo { case binary.LittleEndian: return bpfRegisters((src << 4) | (dst & 0xF)), nil case binary.BigEndian: return bpfRegisters((dst << 4) | (src & 0xF)), nil default: return 0, fmt.Errorf("unrecognized ByteOrder %T", bo) } } golang-github-cilium-ebpf-0.21.0+ds1/asm/instruction_test.go000066400000000000000000000334521520243672000237350ustar00rootroot00000000000000package asm import ( "bytes" "encoding/binary" "encoding/hex" "fmt" "io" "math" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/platform" ) var test64bitImmProg = []byte{ // r0 = math.MinInt32 - 1 0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, } func TestRead64bitImmediate(t *testing.T) { var ins Instruction err := ins.Unmarshal(bytes.NewReader(test64bitImmProg), binary.LittleEndian, platform.Linux) if err != nil { t.Fatal(err) } if c := ins.Constant; c != math.MinInt32-1 { t.Errorf("Expected immediate to be %v, got %v", int64(math.MinInt32)-1, c) } } func BenchmarkRead64bitImmediate(b *testing.B) { r := &bytes.Reader{} for b.Loop() { r.Reset(test64bitImmProg) var ins Instruction if err := ins.Unmarshal(r, binary.LittleEndian, platform.Linux); err != nil { b.Fatal(err) } } } func TestWrite64bitImmediate(t *testing.T) { insns := Instructions{ LoadImm(R0, math.MinInt32-1, DWord), } var buf bytes.Buffer if err := insns.Marshal(&buf, binary.LittleEndian); err != nil { t.Fatal(err) } if prog := buf.Bytes(); !bytes.Equal(prog, test64bitImmProg) { t.Errorf("Marshalled program does not match:\n%s", hex.Dump(prog)) } } func BenchmarkWrite64BitImmediate(b *testing.B) { ins := LoadImm(R0, math.MinInt32-1, DWord) var buf bytes.Buffer for b.Loop() { buf.Reset() if _, err := ins.Marshal(&buf, binary.LittleEndian); err != nil { b.Fatal(err) } } } func TestAppendInstructions(t *testing.T) { r := bytes.NewReader(test64bitImmProg) insns, err := AppendInstructions(nil, r, binary.LittleEndian, platform.Linux) qt.Assert(t, qt.IsNil(err)) if len(insns) != 1 { t.Fatalf("Expected one instruction, got %d", len(insns)) } } func TestSignedJump(t *testing.T) { insns := Instructions{ JSGT.Imm(R0, -1, "foo"), } insns[0].Offset = 1 err := insns.Marshal(io.Discard, binary.LittleEndian) if err != nil { t.Error("Can't marshal signed jump:", err) } } func TestInstructionLoadMapValue(t *testing.T) { ins := LoadMapValue(R0, 1, 123) if !ins.IsLoadFromMap() { t.Error("isLoadFromMap returns false") } if fd := ins.mapFd(); fd != 1 { t.Error("Expected map fd to be 1, got", fd) } if off := ins.mapOffset(); off != 123 { t.Fatal("Expected map offset to be 123 after changing the pointer, got", off) } } func TestInstructionWithMetadata(t *testing.T) { ins := LoadImm(R0, 123, DWord).WithSymbol("abc") ins2 := LoadImm(R0, 567, DWord).WithMetadata(ins.Metadata) if want, got := "abc", ins2.Symbol(); want != got { t.Fatalf("unexpected Symbol value on ins2: want: %s, got: %s", want, got) } if want, got := ins.Metadata, ins2.Metadata; want != got { t.Fatal("expected ins and isn2 Metadata to match") } } func TestReadCallToNegativeOne(t *testing.T) { raw := []byte{ 0x85, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, } var ins Instruction err := ins.Unmarshal(bytes.NewReader(raw), binary.LittleEndian, platform.Linux) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(ins.Constant, -1)) } // You can use format flags to change the way an eBPF // program is stringified. func ExampleInstructions_Format() { insns := Instructions{ FnMapLookupElem.Call().WithSymbol("my_func").WithSource(Comment("bpf_map_lookup_elem()")), LoadImm(R0, 42, DWord).WithSource(Comment("abc = 42")), Return(), } fmt.Println("Default format:") fmt.Printf("%v\n", insns) fmt.Println("Don't indent instructions:") fmt.Printf("%.0v\n", insns) fmt.Println("Indent using spaces:") fmt.Printf("% v\n", insns) fmt.Println("Control symbol indentation:") fmt.Printf("%2v\n", insns) // Output: Default format: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Don't indent instructions: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Indent using spaces: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit // // Control symbol indentation: // my_func: // ; bpf_map_lookup_elem() // 0: Call FnMapLookupElem // ; abc = 42 // 1: LdImmDW dst: r0 imm: 42 // 3: Exit } func TestReadSrcDst(t *testing.T) { testSrcDstProg := []byte{ // on little-endian: r0 = r1 // on big-endian: be: r1 = r0 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } testcases := []struct { bo binary.ByteOrder dst, src Register }{ {binary.BigEndian, R1, R0}, {binary.LittleEndian, R0, R1}, } for _, tc := range testcases { t.Run(tc.bo.String(), func(t *testing.T) { var ins Instruction err := ins.Unmarshal(bytes.NewReader(testSrcDstProg), tc.bo, platform.Linux) if err != nil { t.Fatal(err) } if ins.Dst != tc.dst { t.Errorf("Expected destination to be %v, got %v", tc.dst, ins.Dst) } if ins.Src != tc.src { t.Errorf("Expected source to be %v, got %v", tc.src, ins.Src) } }) } } func TestInstructionIterator(t *testing.T) { insns := Instructions{ LoadImm(R0, 0, Word), LoadImm(R0, 0, DWord), Return(), } offsets := []RawInstructionOffset{0, 1, 3} iter := insns.Iterate() for i := 0; i < len(insns); i++ { if !iter.Next() { t.Fatalf("Expected %dth call to Next to return true", i) } if iter.Ins == nil { t.Errorf("Expected iter.Ins to be non-nil") } if iter.Index != i { t.Errorf("Expected iter.Index to be %d, got %d", i, iter.Index) } if iter.Offset != offsets[i] { t.Errorf("Expected iter.Offset to be %d, got %d", offsets[i], iter.Offset) } } } func TestMetadataCopyOnWrite(t *testing.T) { // Setting metadata should copy Instruction and modify the metadata pointer // of the new object without touching the old Instruction. // Reference ins := Ja.Label("my_func") ins2 := ins.WithReference("my_func2") qt.Assert(t, qt.Equals(ins.Reference(), "my_func"), qt.Commentf("WithReference updated ins")) qt.Assert(t, qt.Equals(ins2.Reference(), "my_func2"), qt.Commentf("WithReference didn't update ins2")) // Symbol ins = Ja.Label("").WithSymbol("my_sym") ins2 = ins.WithSymbol("my_sym2") qt.Assert(t, qt.Equals(ins.Symbol(), "my_sym"), qt.Commentf("WithSymbol updated ins")) qt.Assert(t, qt.Equals(ins2.Symbol(), "my_sym2"), qt.Commentf("WithSymbol didn't update ins2")) // Map ins = LoadMapPtr(R1, 0) ins2 = ins testMap := testFDer(1) qt.Assert(t, qt.IsNil(ins2.AssociateMap(testMap)), qt.Commentf("failed to associate map with ins2")) qt.Assert(t, qt.IsNil(ins.Map()), qt.Commentf("AssociateMap updated ins")) qt.Assert(t, qt.Equals[FDer](ins2.Map(), testMap), qt.Commentf("AssociateMap didn't update ins2")) } type testFDer int func (t testFDer) FD() int { return int(t) } func TestAtomics(t *testing.T) { rawInsns := []byte{ 0xc3, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) += w2 0xc3, 0x21, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) &= w2 0xc3, 0x21, 0x01, 0x00, 0xa0, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) ^= w2 0xc3, 0x21, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) |= w2 0xdb, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) += r2 0xdb, 0x21, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) &= r2 0xdb, 0x21, 0x01, 0x00, 0xa0, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) ^= r2 0xdb, 0x21, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) |= r2 0xc3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // w0 = atomic_fetch_add((u32 *)(r1 + 0x0), w0) 0xc3, 0x01, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, // w0 = atomic_fetch_and((u32 *)(r1 + 0x0), w0) 0xc3, 0x01, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, // w0 = atomic_fetch_xor((u32 *)(r1 + 0x0), w0) 0xc3, 0x01, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, // w0 = atomic_fetch_or((u32 *)(r1 + 0x0), w0) 0xdb, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // r0 = atomic_fetch_add((u64 *)(r1 + 0x0), r0) 0xdb, 0x01, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, // r0 = atomic_fetch_and((u64 *)(r1 + 0x0), r0) 0xdb, 0x01, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, // r0 = atomic_fetch_xor((u64 *)(r1 + 0x0), r0) 0xdb, 0x01, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, // r0 = atomic_fetch_or((u64 *)(r1 + 0x0), r0) 0xc3, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, // w0 = xchg32_32(r1 + 0x0, w0) 0xdb, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, // r0 = xchg_64(r1 + 0x0, r0) 0xc3, 0x11, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, // w0 = cmpxchg32_32(r1 + 0x0, w0, w1) 0xdb, 0x11, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, // r0 = cmpxchg_64(r1 + 0x0, r0, r1) } insns, err := AppendInstructions(nil, bytes.NewReader(rawInsns), binary.LittleEndian, platform.Linux) if err != nil { t.Fatal(err) } lines := []string{ "StXAtomicAddW dst: r1 src: r2 off: 1", "StXAtomicAndW dst: r1 src: r2 off: 1", "StXAtomicXorW dst: r1 src: r2 off: 1", "StXAtomicOrW dst: r1 src: r2 off: 1", "StXAtomicAddDW dst: r1 src: r2 off: 1", "StXAtomicAndDW dst: r1 src: r2 off: 1", "StXAtomicXorDW dst: r1 src: r2 off: 1", "StXAtomicOrDW dst: r1 src: r2 off: 1", "StXAtomicFetchAddW dst: r1 src: r0 off: 0", "StXAtomicFetchAndW dst: r1 src: r0 off: 0", "StXAtomicFetchXorW dst: r1 src: r0 off: 0", "StXAtomicFetchOrW dst: r1 src: r0 off: 0", "StXAtomicFetchAddDW dst: r1 src: r0 off: 0", "StXAtomicFetchAndDW dst: r1 src: r0 off: 0", "StXAtomicFetchXorDW dst: r1 src: r0 off: 0", "StXAtomicFetchOrDW dst: r1 src: r0 off: 0", "StXAtomicXchgW dst: r1 src: r0 off: 0", "StXAtomicXchgDW dst: r1 src: r0 off: 0", "StXAtomicCmpXchgW dst: r1 src: r1 off: 0", "StXAtomicCmpXchgDW dst: r1 src: r1 off: 0", } for i, ins := range insns { if want, got := lines[i], fmt.Sprint(ins); want != got { t.Errorf("Expected %q, got %q", want, got) } } // Marshal and unmarshal again to make sure the instructions are // still valid. var buf bytes.Buffer err = insns.Marshal(&buf, binary.LittleEndian) if err != nil { t.Fatal(err) } if !bytes.Equal(buf.Bytes(), rawInsns) { t.Error("Expected instructions to be equal after marshalling") } } func TestISAv4(t *testing.T) { rawInsns := []byte{ 0xd7, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // r1 = bswap16 r1 0xd7, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // r2 = bswap32 r2 0xd7, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // r3 = bswap64 r3 0x91, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 = *(s8 *)(r4 + 0x0) 0x89, 0x52, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 = *(s16 *)(r5 + 0x4) 0x81, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // r3 = *(s32 *)(r6 + 0x8) 0x91, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 = *(s8 *)(r4 + 0x0) 0x89, 0x52, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 = *(s16 *)(r5 + 0x4) 0xbf, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 = (s8)r4 0xbf, 0x52, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 = (s16)r5 0xbf, 0x63, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // r3 = (s32)r6 0xbc, 0x31, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // w1 = (s8)w3 0xbc, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // w2 = (s16)w4 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, // gotol +3 0x3f, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 s/= r3 0x9f, 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 s%= r4 0x3c, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // w1 s/= w3 0x9c, 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // w2 s%= w4 0xd3, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // w0 = load_acquire((u8 *)(r1 + 0x0)) 0xcb, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // w0 = load_acquire((u16 *)(r1 + 0x0)) 0xc3, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // w0 = load_acquire((u32 *)(r1 + 0x0)) 0xdb, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // r0 = load_acquire((u64 *)(r1 + 0x0)) 0xd3, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u8 *)(r1 + 0x0), w2) 0xcb, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u16 *)(r1 + 0x0), w2) 0xc3, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u32 *)(r1 + 0x0), w2) 0xdb, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u64 *)(r1 + 0x0), r2) } insns, err := AppendInstructions(nil, bytes.NewReader(rawInsns), binary.LittleEndian, platform.Linux) if err != nil { t.Fatal(err) } lines := []string{ "BSwap16 dst: r1 ", "BSwap32 dst: r2 ", "BSwap64 dst: r3 ", "LdXMemSXB dst: r1 src: r4 off: 0 imm: 0", "LdXMemSXH dst: r2 src: r5 off: 4 imm: 0", "LdXMemSXW dst: r3 src: r6 off: 8 imm: 0", "LdXMemSXB dst: r1 src: r4 off: 0 imm: 0", "LdXMemSXH dst: r2 src: r5 off: 4 imm: 0", "MovSX8Reg dst: r1 src: r4", "MovSX16Reg dst: r2 src: r5", "MovSX32Reg dst: r3 src: r6", "MovSX8Reg32 dst: r1 src: r3", "MovSX16Reg32 dst: r2 src: r4", "Ja32 imm: 3", "SDivReg dst: r1 src: r3", "SModReg dst: r2 src: r4", "SDivReg32 dst: r1 src: r3", "SModReg32 dst: r2 src: r4", "StXAtomicLdAcqB dst: r0 src: r1 off: 0", "StXAtomicLdAcqH dst: r0 src: r1 off: 0", "StXAtomicLdAcqW dst: r0 src: r1 off: 0", "StXAtomicLdAcqDW dst: r0 src: r1 off: 0", "StXAtomicStRelB dst: r1 src: r2 off: 0", "StXAtomicStRelH dst: r1 src: r2 off: 0", "StXAtomicStRelW dst: r1 src: r2 off: 0", "StXAtomicStRelDW dst: r1 src: r2 off: 0", } for i, ins := range insns { if want, got := lines[i], fmt.Sprint(ins); want != got { t.Errorf("Expected %q, got %q", want, got) } } // Marshal and unmarshal again to make sure the instructions are // still valid. var buf bytes.Buffer err = insns.Marshal(&buf, binary.LittleEndian) if err != nil { t.Fatal(err) } if !bytes.Equal(buf.Bytes(), rawInsns) { t.Error("Expected instructions to be equal after marshalling") } } func TestLongJumpPatching(t *testing.T) { insns := Instructions{ LongJump("exit"), Xor.Reg(R0, R0), Xor.Reg(R0, R0), Xor.Reg(R0, R0), Return().WithSymbol("exit"), } err := insns.encodeFunctionReferences() if err != nil { t.Fatal(err) } if insns[0].Constant != 3 { t.Errorf("Expected offset to be 3, got %d", insns[1].Constant) } } golang-github-cilium-ebpf-0.21.0+ds1/asm/jump.go000066400000000000000000000070671520243672000212730ustar00rootroot00000000000000package asm //go:generate go tool stringer -output jump_string.go -type=JumpOp // JumpOp affect control flow. // // msb lsb // +----+-+---+ // |OP |s|cls| // +----+-+---+ type JumpOp uint8 const jumpMask OpCode = 0xf0 const ( // InvalidJumpOp is returned by getters when invoked // on non branch OpCodes InvalidJumpOp JumpOp = 0xff // Ja jumps by offset unconditionally Ja JumpOp = 0x00 // JEq jumps by offset if r == imm JEq JumpOp = 0x10 // JGT jumps by offset if r > imm JGT JumpOp = 0x20 // JGE jumps by offset if r >= imm JGE JumpOp = 0x30 // JSet jumps by offset if r & imm JSet JumpOp = 0x40 // JNE jumps by offset if r != imm JNE JumpOp = 0x50 // JSGT jumps by offset if signed r > signed imm JSGT JumpOp = 0x60 // JSGE jumps by offset if signed r >= signed imm JSGE JumpOp = 0x70 // Call builtin or user defined function from imm Call JumpOp = 0x80 // Exit ends execution, with value in r0 Exit JumpOp = 0x90 // JLT jumps by offset if r < imm JLT JumpOp = 0xa0 // JLE jumps by offset if r <= imm JLE JumpOp = 0xb0 // JSLT jumps by offset if signed r < signed imm JSLT JumpOp = 0xc0 // JSLE jumps by offset if signed r <= signed imm JSLE JumpOp = 0xd0 ) // Return emits an exit instruction. // // Requires a return value in R0. func Return() Instruction { return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(Exit), } } // Op returns the OpCode for a given jump source. func (op JumpOp) Op(source Source) OpCode { return OpCode(JumpClass).SetJumpOp(op).SetSource(source) } // Imm compares 64 bit dst to 64 bit value (sign extended), and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Imm(dst Register, value int32, label string) Instruction { return Instruction{ OpCode: op.opCode(JumpClass, ImmSource), Dst: dst, Offset: -1, Constant: int64(value), }.WithReference(label) } // Imm32 compares 32 bit dst to 32 bit value, and adjusts PC by offset if the condition is fulfilled. // Requires kernel 5.1. func (op JumpOp) Imm32(dst Register, value int32, label string) Instruction { return Instruction{ OpCode: op.opCode(Jump32Class, ImmSource), Dst: dst, Offset: -1, Constant: int64(value), }.WithReference(label) } // Reg compares 64 bit dst to 64 bit src, and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Reg(dst, src Register, label string) Instruction { return Instruction{ OpCode: op.opCode(JumpClass, RegSource), Dst: dst, Src: src, Offset: -1, }.WithReference(label) } // Reg32 compares 32 bit dst to 32 bit src, and adjusts PC by offset if the condition is fulfilled. // Requires kernel 5.1. func (op JumpOp) Reg32(dst, src Register, label string) Instruction { return Instruction{ OpCode: op.opCode(Jump32Class, RegSource), Dst: dst, Src: src, Offset: -1, }.WithReference(label) } func (op JumpOp) opCode(class Class, source Source) OpCode { if op == Exit || op == Call { return InvalidOpCode } return OpCode(class).SetJumpOp(op).SetSource(source) } // LongJump returns a jump always instruction with a range of [-2^31, 2^31 - 1]. func LongJump(label string) Instruction { return Instruction{ OpCode: Ja.opCode(Jump32Class, ImmSource), Constant: -1, }.WithReference(label) } // Label adjusts PC to the address of the label. func (op JumpOp) Label(label string) Instruction { if op == Call { return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(Call), Src: PseudoCall, Constant: -1, }.WithReference(label) } return Instruction{ OpCode: OpCode(JumpClass).SetJumpOp(op), Offset: -1, }.WithReference(label) } golang-github-cilium-ebpf-0.21.0+ds1/asm/jump_string.go000066400000000000000000000022761520243672000226560ustar00rootroot00000000000000// Code generated by "stringer -output jump_string.go -type=JumpOp"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidJumpOp-255] _ = x[Ja-0] _ = x[JEq-16] _ = x[JGT-32] _ = x[JGE-48] _ = x[JSet-64] _ = x[JNE-80] _ = x[JSGT-96] _ = x[JSGE-112] _ = x[Call-128] _ = x[Exit-144] _ = x[JLT-160] _ = x[JLE-176] _ = x[JSLT-192] _ = x[JSLE-208] } const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp" var _JumpOp_map = map[JumpOp]string{ 0: _JumpOp_name[0:2], 16: _JumpOp_name[2:5], 32: _JumpOp_name[5:8], 48: _JumpOp_name[8:11], 64: _JumpOp_name[11:15], 80: _JumpOp_name[15:18], 96: _JumpOp_name[18:22], 112: _JumpOp_name[22:26], 128: _JumpOp_name[26:30], 144: _JumpOp_name[30:34], 160: _JumpOp_name[34:37], 176: _JumpOp_name[37:40], 192: _JumpOp_name[40:44], 208: _JumpOp_name[44:48], 255: _JumpOp_name[48:61], } func (i JumpOp) String() string { if str, ok := _JumpOp_map[i]; ok { return str } return "JumpOp(" + strconv.FormatInt(int64(i), 10) + ")" } golang-github-cilium-ebpf-0.21.0+ds1/asm/load_store.go000066400000000000000000000212201520243672000224360ustar00rootroot00000000000000package asm import "fmt" //go:generate go tool stringer -output load_store_string.go -type=Mode,Size // Mode for load and store operations // // msb lsb // +---+--+---+ // |MDE|sz|cls| // +---+--+---+ type Mode uint8 const modeMask OpCode = 0xe0 const ( // InvalidMode is returned by getters when invoked // on non load / store OpCodes InvalidMode Mode = 0xff // ImmMode - immediate value ImmMode Mode = 0x00 // AbsMode - immediate value + offset AbsMode Mode = 0x20 // IndMode - indirect (imm+src) IndMode Mode = 0x40 // MemMode - load from memory MemMode Mode = 0x60 // MemSXMode - load from memory, sign extension MemSXMode Mode = 0x80 // AtomicMode - add atomically across processors. AtomicMode Mode = 0xc0 ) const atomicMask OpCode = 0x0001_ff00 type AtomicOp uint32 const ( InvalidAtomic AtomicOp = 0xffff_ffff // AddAtomic - add src to memory address dst atomically AddAtomic AtomicOp = AtomicOp(Add) << 8 // FetchAdd - add src to memory address dst atomically, store result in src FetchAdd AtomicOp = AddAtomic | fetch // AndAtomic - bitwise AND src with memory address at dst atomically AndAtomic AtomicOp = AtomicOp(And) << 8 // FetchAnd - bitwise AND src with memory address at dst atomically, store result in src FetchAnd AtomicOp = AndAtomic | fetch // OrAtomic - bitwise OR src with memory address at dst atomically OrAtomic AtomicOp = AtomicOp(Or) << 8 // FetchOr - bitwise OR src with memory address at dst atomically, store result in src FetchOr AtomicOp = OrAtomic | fetch // XorAtomic - bitwise XOR src with memory address at dst atomically XorAtomic AtomicOp = AtomicOp(Xor) << 8 // FetchXor - bitwise XOR src with memory address at dst atomically, store result in src FetchXor AtomicOp = XorAtomic | fetch // Xchg - atomically exchange the old value with the new value // // src gets populated with the old value of *(size *)(dst + offset). Xchg AtomicOp = 0x0000_e000 | fetch // CmpXchg - atomically compare and exchange the old value with the new value // // Compares R0 and *(size *)(dst + offset), writes src to *(size *)(dst + offset) on match. // R0 gets populated with the old value of *(size *)(dst + offset), even if no exchange occurs. CmpXchg AtomicOp = 0x0000_f000 | fetch // fetch modifier for copy-modify-write atomics fetch AtomicOp = 0x0000_0100 // loadAcquire - atomically load with acquire semantics loadAcquire AtomicOp = 0x0001_0000 // storeRelease - atomically store with release semantics storeRelease AtomicOp = 0x0001_1000 ) func (op AtomicOp) String() string { var name string switch op { case AddAtomic, AndAtomic, OrAtomic, XorAtomic: name = ALUOp(op >> 8).String() case FetchAdd, FetchAnd, FetchOr, FetchXor: name = "Fetch" + ALUOp((op^fetch)>>8).String() case Xchg: name = "Xchg" case CmpXchg: name = "CmpXchg" case loadAcquire: name = "LdAcq" case storeRelease: name = "StRel" default: name = fmt.Sprintf("AtomicOp(%#x)", uint32(op)) } return name } func (op AtomicOp) OpCode(size Size) OpCode { switch op { case AddAtomic, AndAtomic, OrAtomic, XorAtomic, FetchAdd, FetchAnd, FetchOr, FetchXor, Xchg, CmpXchg: switch size { case Byte, Half: // 8-bit and 16-bit atomic copy-modify-write atomics are not supported return InvalidOpCode } } return OpCode(StXClass).SetMode(AtomicMode).SetSize(size).SetAtomicOp(op) } // Mem emits `*(size *)(dst + offset) (op) src`. func (op AtomicOp) Mem(dst, src Register, size Size, offset int16) Instruction { return Instruction{ OpCode: op.OpCode(size), Dst: dst, Src: src, Offset: offset, } } // Emits `lock-acquire dst = *(size *)(src + offset)`. func LoadAcquire(dst, src Register, size Size, offset int16) Instruction { return Instruction{ OpCode: loadAcquire.OpCode(size), Dst: dst, Src: src, Offset: offset, } } // Emits `lock-release *(size *)(dst + offset) = src`. func StoreRelease(dst, src Register, size Size, offset int16) Instruction { return Instruction{ OpCode: storeRelease.OpCode(size), Dst: dst, Src: src, Offset: offset, } } // Size of load and store operations // // msb lsb // +---+--+---+ // |mde|SZ|cls| // +---+--+---+ type Size uint8 const sizeMask OpCode = 0x18 const ( // InvalidSize is returned by getters when invoked // on non load / store OpCodes InvalidSize Size = 0xff // DWord - double word; 64 bits DWord Size = 0x18 // Word - word; 32 bits Word Size = 0x00 // Half - half-word; 16 bits Half Size = 0x08 // Byte - byte; 8 bits Byte Size = 0x10 ) // Sizeof returns the size in bytes. func (s Size) Sizeof() int { switch s { case DWord: return 8 case Word: return 4 case Half: return 2 case Byte: return 1 default: return -1 } } // LoadMemOp returns the OpCode to load a value of given size from memory. func LoadMemOp(size Size) OpCode { return OpCode(LdXClass).SetMode(MemMode).SetSize(size) } // LoadMemSXOp returns the OpCode to load a value of given size from memory sign extended. func LoadMemSXOp(size Size) OpCode { return OpCode(LdXClass).SetMode(MemSXMode).SetSize(size) } // LoadMem emits `dst = *(size *)(src + offset)`. func LoadMem(dst, src Register, offset int16, size Size) Instruction { return Instruction{ OpCode: LoadMemOp(size), Dst: dst, Src: src, Offset: offset, } } // LoadMemSX emits `dst = *(size *)(src + offset)` but sign extends dst. func LoadMemSX(dst, src Register, offset int16, size Size) Instruction { if size == DWord { return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: LoadMemSXOp(size), Dst: dst, Src: src, Offset: offset, } } // LoadImmOp returns the OpCode to load an immediate of given size. // // As of kernel 4.20, only DWord size is accepted. func LoadImmOp(size Size) OpCode { return OpCode(LdClass).SetMode(ImmMode).SetSize(size) } // LoadImm emits `dst = (size)value`. // // As of kernel 4.20, only DWord size is accepted. func LoadImm(dst Register, value int64, size Size) Instruction { return Instruction{ OpCode: LoadImmOp(size), Dst: dst, Constant: value, } } // LoadMapPtr stores a pointer to a map in dst. func LoadMapPtr(dst Register, fd int) Instruction { if fd < 0 { return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: LoadImmOp(DWord), Dst: dst, Src: PseudoMapFD, Constant: int64(uint32(fd)), } } // LoadMapValue stores a pointer to the value at a certain offset of a map. func LoadMapValue(dst Register, fd int, offset uint32) Instruction { if fd < 0 { return Instruction{OpCode: InvalidOpCode} } fdAndOffset := (uint64(offset) << 32) | uint64(uint32(fd)) return Instruction{ OpCode: LoadImmOp(DWord), Dst: dst, Src: PseudoMapValue, Constant: int64(fdAndOffset), } } // LoadIndOp returns the OpCode for loading a value of given size from an sk_buff. func LoadIndOp(size Size) OpCode { return OpCode(LdClass).SetMode(IndMode).SetSize(size) } // LoadInd emits `dst = ntoh(*(size *)(((sk_buff *)R6)->data + src + offset))`. func LoadInd(dst, src Register, offset int32, size Size) Instruction { return Instruction{ OpCode: LoadIndOp(size), Dst: dst, Src: src, Constant: int64(offset), } } // LoadAbsOp returns the OpCode for loading a value of given size from an sk_buff. func LoadAbsOp(size Size) OpCode { return OpCode(LdClass).SetMode(AbsMode).SetSize(size) } // LoadAbs emits `r0 = ntoh(*(size *)(((sk_buff *)R6)->data + offset))`. func LoadAbs(offset int32, size Size) Instruction { return Instruction{ OpCode: LoadAbsOp(size), Dst: R0, Constant: int64(offset), } } // StoreMemOp returns the OpCode for storing a register of given size in memory. func StoreMemOp(size Size) OpCode { return OpCode(StXClass).SetMode(MemMode).SetSize(size) } // StoreMem emits `*(size *)(dst + offset) = src` func StoreMem(dst Register, offset int16, src Register, size Size) Instruction { return Instruction{ OpCode: StoreMemOp(size), Dst: dst, Src: src, Offset: offset, } } // StoreImmOp returns the OpCode for storing an immediate of given size in memory. func StoreImmOp(size Size) OpCode { return OpCode(StClass).SetMode(MemMode).SetSize(size) } // StoreImm emits `*(size *)(dst + offset) = value`. func StoreImm(dst Register, offset int16, value int64, size Size) Instruction { if size == DWord { return Instruction{OpCode: InvalidOpCode} } return Instruction{ OpCode: StoreImmOp(size), Dst: dst, Offset: offset, Constant: value, } } // StoreXAddOp returns the OpCode to atomically add a register to a value in memory. func StoreXAddOp(size Size) OpCode { return AddAtomic.OpCode(size) } // StoreXAdd atomically adds src to *dst. func StoreXAdd(dst, src Register, size Size) Instruction { return AddAtomic.Mem(dst, src, size, 0) } golang-github-cilium-ebpf-0.21.0+ds1/asm/load_store_string.go000066400000000000000000000032771520243672000240400ustar00rootroot00000000000000// Code generated by "stringer -output load_store_string.go -type=Mode,Size"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidMode-255] _ = x[ImmMode-0] _ = x[AbsMode-32] _ = x[IndMode-64] _ = x[MemMode-96] _ = x[MemSXMode-128] _ = x[AtomicMode-192] } const ( _Mode_name_0 = "ImmMode" _Mode_name_1 = "AbsMode" _Mode_name_2 = "IndMode" _Mode_name_3 = "MemMode" _Mode_name_4 = "MemSXMode" _Mode_name_5 = "AtomicMode" _Mode_name_6 = "InvalidMode" ) func (i Mode) String() string { switch { case i == 0: return _Mode_name_0 case i == 32: return _Mode_name_1 case i == 64: return _Mode_name_2 case i == 96: return _Mode_name_3 case i == 128: return _Mode_name_4 case i == 192: return _Mode_name_5 case i == 255: return _Mode_name_6 default: return "Mode(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[InvalidSize-255] _ = x[DWord-24] _ = x[Word-0] _ = x[Half-8] _ = x[Byte-16] } const ( _Size_name_0 = "Word" _Size_name_1 = "Half" _Size_name_2 = "Byte" _Size_name_3 = "DWord" _Size_name_4 = "InvalidSize" ) func (i Size) String() string { switch { case i == 0: return _Size_name_0 case i == 8: return _Size_name_1 case i == 16: return _Size_name_2 case i == 24: return _Size_name_3 case i == 255: return _Size_name_4 default: return "Size(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-cilium-ebpf-0.21.0+ds1/asm/metadata.go000066400000000000000000000034551520243672000220750ustar00rootroot00000000000000package asm // Metadata contains metadata about an instruction. type Metadata struct { head *metaElement } type metaElement struct { next *metaElement key, value interface{} } // Find the element containing key. // // Returns nil if there is no such element. func (m *Metadata) find(key interface{}) *metaElement { for e := m.head; e != nil; e = e.next { if e.key == key { return e } } return nil } // Remove an element from the linked list. // // Copies as many elements of the list as necessary to remove r, but doesn't // perform a full copy. func (m *Metadata) remove(r *metaElement) { current := &m.head for e := m.head; e != nil; e = e.next { if e == r { // We've found the element we want to remove. *current = e.next // No need to copy the tail. return } // There is another element in front of the one we want to remove. // We have to copy it to be able to change metaElement.next. cpy := &metaElement{key: e.key, value: e.value} *current = cpy current = &cpy.next } } // Set a key to a value. // // If value is nil, the key is removed. Avoids modifying old metadata by // copying if necessary. func (m *Metadata) Set(key, value interface{}) { if e := m.find(key); e != nil { if e.value == value { // Key is present and the value is the same. Nothing to do. return } // Key is present with a different value. Create a copy of the list // which doesn't have the element in it. m.remove(e) } // m.head is now a linked list that doesn't contain key. if value == nil { return } m.head = &metaElement{key: key, value: value, next: m.head} } // Get the value of a key. // // Returns nil if no value with the given key is present. func (m *Metadata) Get(key interface{}) interface{} { if e := m.find(key); e != nil { return e.value } return nil } golang-github-cilium-ebpf-0.21.0+ds1/asm/metadata_test.go000066400000000000000000000040751520243672000231330ustar00rootroot00000000000000package asm import ( "testing" "unsafe" "github.com/go-quicktest/qt" ) func TestMetadata(t *testing.T) { var m Metadata // Metadata should be the size of a pointer. qt.Assert(t, qt.Equals(unsafe.Sizeof(m), unsafe.Sizeof(uintptr(0)))) // A lookup in a nil meta should return nil. qt.Assert(t, qt.IsNil(m.Get(bool(false)))) // We can look up anything we inserted. m.Set(bool(false), int(0)) m.Set(int(1), int(1)) qt.Assert(t, qt.Equals(m.Get(bool(false)), 0)) qt.Assert(t, qt.Equals(m.Get(1), 1)) // We have copy on write semantics old := m m.Set(bool(false), int(1)) qt.Assert(t, qt.Equals(m.Get(bool(false)), 1)) qt.Assert(t, qt.Equals(m.Get(int(1)), 1)) qt.Assert(t, qt.Equals(old.Get(bool(false)), 0)) qt.Assert(t, qt.Equals(old.Get(int(1)), 1)) // Newtypes are handled distinctly. type b bool m.Set(b(false), int(42)) qt.Assert(t, qt.Equals(m.Get(bool(false)), 1)) qt.Assert(t, qt.Equals(m.Get(int(1)), 1)) qt.Assert(t, qt.Equals(m.Get(b(false)), 42)) // Setting nil removes a key. m.Set(bool(false), nil) qt.Assert(t, qt.IsNil(m.Get(bool(false)))) qt.Assert(t, qt.Equals(m.Get(int(1)), 1)) qt.Assert(t, qt.Equals(m.Get(b(false)), 42)) } func BenchmarkMetadata(b *testing.B) { // Assume that three bits of metadata on a single instruction is // our worst case. const worstCaseItems = 3 type t struct{} b.Run("add first", func(b *testing.B) { b.ReportAllocs() for b.Loop() { var v Metadata v.Set(t{}, 0) } }) b.Run("add last", func(b *testing.B) { var m Metadata for i := 0; i < worstCaseItems-1; i++ { m.Set(i, i) } b.ReportAllocs() for b.Loop() { v := m v.Set(t{}, 0) } }) b.Run("add existing", func(b *testing.B) { var m Metadata for i := 0; i < worstCaseItems-1; i++ { m.Set(i, i) } m.Set(t{}, 0) b.ReportAllocs() for b.Loop() { v := m v.Set(t{}, 0) } }) b.Run("get miss", func(b *testing.B) { var m Metadata for i := 0; i < worstCaseItems; i++ { m.Set(i, i) } b.ReportAllocs() for b.Loop() { if m.Get(t{}) != nil { b.Fatal("got result from miss") } } }) } golang-github-cilium-ebpf-0.21.0+ds1/asm/opcode.go000066400000000000000000000176671520243672000216000ustar00rootroot00000000000000package asm import ( "fmt" "strings" ) //go:generate go tool stringer -output opcode_string.go -type=Class // Class of operations // // msb lsb // +---+--+---+ // | ?? |CLS| // +---+--+---+ type Class uint8 const classMask OpCode = 0x07 const ( // LdClass loads immediate values into registers. // Also used for non-standard load operations from cBPF. LdClass Class = 0x00 // LdXClass loads memory into registers. LdXClass Class = 0x01 // StClass stores immediate values to memory. StClass Class = 0x02 // StXClass stores registers to memory. StXClass Class = 0x03 // ALUClass describes arithmetic operators. ALUClass Class = 0x04 // JumpClass describes jump operators. JumpClass Class = 0x05 // Jump32Class describes jump operators with 32-bit comparisons. // Requires kernel 5.1. Jump32Class Class = 0x06 // ALU64Class describes arithmetic operators in 64-bit mode. ALU64Class Class = 0x07 ) // IsLoad checks if this is either LdClass or LdXClass. func (cls Class) IsLoad() bool { return cls == LdClass || cls == LdXClass } // IsStore checks if this is either StClass or StXClass. func (cls Class) IsStore() bool { return cls == StClass || cls == StXClass } func (cls Class) isLoadOrStore() bool { return cls.IsLoad() || cls.IsStore() } // IsALU checks if this is either ALUClass or ALU64Class. func (cls Class) IsALU() bool { return cls == ALUClass || cls == ALU64Class } // IsJump checks if this is either JumpClass or Jump32Class. func (cls Class) IsJump() bool { return cls == JumpClass || cls == Jump32Class } func (cls Class) isJumpOrALU() bool { return cls.IsJump() || cls.IsALU() } // OpCode represents a single operation. // It is not a 1:1 mapping to real eBPF opcodes. // // The encoding varies based on a 3-bit Class: // // 7 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 // ??? | CLS // // For ALUClass and ALUCLass32: // // 7 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 // 0 | OPC |S| CLS // // For LdClass, LdXclass, StClass and StXClass: // // 7 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 // 0 | MDE |SIZ| CLS // // For StXClass where MDE == AtomicMode: // // 7 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 // 0 | ATOMIC OP | MDE |SIZ| CLS // // For JumpClass, Jump32Class: // // 7 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 // 0 | OPC |S| CLS type OpCode uint32 // InvalidOpCode is returned by setters on OpCode const InvalidOpCode OpCode = 0xffff // bpfOpCode returns the actual BPF opcode. func (op OpCode) bpfOpCode() (byte, error) { const opCodeMask = 0xff if !valid(op, opCodeMask) { return 0, fmt.Errorf("invalid opcode %x", op) } return byte(op & opCodeMask), nil } // rawInstructions returns the number of BPF instructions required // to encode this opcode. func (op OpCode) rawInstructions() int { if op.IsDWordLoad() { return 2 } return 1 } func (op OpCode) IsDWordLoad() bool { return op == LoadImmOp(DWord) } // Class returns the class of operation. func (op OpCode) Class() Class { return Class(op & classMask) } // Mode returns the mode for load and store operations. func (op OpCode) Mode() Mode { if !op.Class().isLoadOrStore() { return InvalidMode } return Mode(op & modeMask) } // Size returns the size for load and store operations. func (op OpCode) Size() Size { if !op.Class().isLoadOrStore() { return InvalidSize } return Size(op & sizeMask) } // AtomicOp returns the type of atomic operation. func (op OpCode) AtomicOp() AtomicOp { if op.Class() != StXClass || op.Mode() != AtomicMode { return InvalidAtomic } return AtomicOp(op & atomicMask) } // Source returns the source for branch and ALU operations. func (op OpCode) Source() Source { if !op.Class().isJumpOrALU() || op.ALUOp() == Swap { return InvalidSource } return Source(op & sourceMask) } // ALUOp returns the ALUOp. func (op OpCode) ALUOp() ALUOp { if !op.Class().IsALU() { return InvalidALUOp } return ALUOp(op & aluMask) } // Endianness returns the Endianness for a byte swap instruction. func (op OpCode) Endianness() Endianness { if op.ALUOp() != Swap { return InvalidEndian } return Endianness(op & endianMask) } // JumpOp returns the JumpOp. // Returns InvalidJumpOp if it doesn't encode a jump. func (op OpCode) JumpOp() JumpOp { if !op.Class().IsJump() { return InvalidJumpOp } jumpOp := JumpOp(op & jumpMask) // Some JumpOps are only supported by JumpClass, not Jump32Class. if op.Class() == Jump32Class && (jumpOp == Exit || jumpOp == Call) { return InvalidJumpOp } return jumpOp } // SetMode sets the mode on load and store operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetMode(mode Mode) OpCode { if !op.Class().isLoadOrStore() || !valid(OpCode(mode), modeMask) { return InvalidOpCode } return (op & ^modeMask) | OpCode(mode) } // SetSize sets the size on load and store operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetSize(size Size) OpCode { if !op.Class().isLoadOrStore() || !valid(OpCode(size), sizeMask) { return InvalidOpCode } return (op & ^sizeMask) | OpCode(size) } func (op OpCode) SetAtomicOp(atomic AtomicOp) OpCode { if op.Class() != StXClass || op.Mode() != AtomicMode || !valid(OpCode(atomic), atomicMask) { return InvalidOpCode } return (op & ^atomicMask) | OpCode(atomic) } // SetSource sets the source on jump and ALU operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetSource(source Source) OpCode { if !op.Class().isJumpOrALU() || !valid(OpCode(source), sourceMask) { return InvalidOpCode } return (op & ^sourceMask) | OpCode(source) } // SetALUOp sets the ALUOp on ALU operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetALUOp(alu ALUOp) OpCode { if !op.Class().IsALU() || !valid(OpCode(alu), aluMask) { return InvalidOpCode } return (op & ^aluMask) | OpCode(alu) } // SetJumpOp sets the JumpOp on jump operations. // // Returns InvalidOpCode if op is of the wrong class. func (op OpCode) SetJumpOp(jump JumpOp) OpCode { if !op.Class().IsJump() || !valid(OpCode(jump), jumpMask) { return InvalidOpCode } newOp := (op & ^jumpMask) | OpCode(jump) // Check newOp is legal. if newOp.JumpOp() == InvalidJumpOp { return InvalidOpCode } return newOp } func (op OpCode) String() string { var f strings.Builder switch class := op.Class(); { case class.isLoadOrStore(): f.WriteString(strings.TrimSuffix(class.String(), "Class")) mode := op.Mode() f.WriteString(strings.TrimSuffix(mode.String(), "Mode")) if atomic := op.AtomicOp(); atomic != InvalidAtomic { f.WriteString(strings.TrimSuffix(atomic.String(), "Atomic")) } switch op.Size() { case DWord: f.WriteString("DW") case Word: f.WriteString("W") case Half: f.WriteString("H") case Byte: f.WriteString("B") } case class.IsALU(): if op.ALUOp() == Swap && op.Class() == ALU64Class { // B to make BSwap, uncontitional byte swap f.WriteString("B") } f.WriteString(op.ALUOp().String()) if op.ALUOp() == Swap { if op.Class() == ALUClass { // Width for Endian is controlled by Constant f.WriteString(op.Endianness().String()) } } else { f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) if class == ALUClass { f.WriteString("32") } } case class.IsJump(): f.WriteString(op.JumpOp().String()) if class == Jump32Class { f.WriteString("32") } if jop := op.JumpOp(); jop != Exit && jop != Call && jop != Ja { f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) } default: fmt.Fprintf(&f, "OpCode(%#x)", uint8(op)) } return f.String() } // valid returns true if all bits in value are covered by mask. func valid(value, mask OpCode) bool { return value & ^mask == 0 } golang-github-cilium-ebpf-0.21.0+ds1/asm/opcode_string.go000066400000000000000000000014721520243672000231510ustar00rootroot00000000000000// Code generated by "stringer -output opcode_string.go -type=Class"; DO NOT EDIT. package asm import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[LdClass-0] _ = x[LdXClass-1] _ = x[StClass-2] _ = x[StXClass-3] _ = x[ALUClass-4] _ = x[JumpClass-5] _ = x[Jump32Class-6] _ = x[ALU64Class-7] } const _Class_name = "LdClassLdXClassStClassStXClassALUClassJumpClassJump32ClassALU64Class" var _Class_index = [...]uint8{0, 7, 15, 22, 30, 38, 47, 58, 68} func (i Class) String() string { idx := int(i) - 0 if i < 0 || idx >= len(_Class_index)-1 { return "Class(" + strconv.FormatInt(int64(i), 10) + ")" } return _Class_name[_Class_index[idx]:_Class_index[idx+1]] } golang-github-cilium-ebpf-0.21.0+ds1/asm/opcode_test.go000066400000000000000000000016371520243672000226250ustar00rootroot00000000000000package asm import ( "fmt" "testing" "github.com/go-quicktest/qt" ) func TestGetSetJumpOp(t *testing.T) { test := func(class Class, op JumpOp, valid bool) { t.Run(fmt.Sprintf("%s-%s", class, op), func(t *testing.T) { opcode := OpCode(class).SetJumpOp(op) if valid { qt.Assert(t, qt.Not(qt.Equals(opcode, InvalidOpCode))) qt.Assert(t, qt.Equals(opcode.JumpOp(), op)) } else { qt.Assert(t, qt.Equals(opcode, InvalidOpCode)) qt.Assert(t, qt.Equals(opcode.JumpOp(), InvalidJumpOp)) } }) } // Exit and call aren't allowed with Jump32 test(Jump32Class, Exit, false) test(Jump32Class, Call, false) // But are with Jump test(JumpClass, Exit, true) test(JumpClass, Call, true) // All other ops work for _, op := range []JumpOp{ Ja, JEq, JGT, JGE, JSet, JNE, JSGT, JSGE, JLT, JLE, JSLT, JSLE, } { test(Jump32Class, op, true) test(JumpClass, op, true) } } golang-github-cilium-ebpf-0.21.0+ds1/asm/register.go000066400000000000000000000015331520243672000221340ustar00rootroot00000000000000package asm import ( "fmt" ) // Register is the source or destination of most operations. type Register uint8 // R0 contains return values. const R0 Register = 0 // Registers for function arguments. const ( R1 Register = R0 + 1 + iota R2 R3 R4 R5 ) // Callee saved registers preserved by function calls. const ( R6 Register = R5 + 1 + iota R7 R8 R9 ) // Read-only frame pointer to access stack. const ( R10 Register = R9 + 1 RFP = R10 ) // Pseudo registers used by 64bit loads and jumps const ( PseudoMapFD = R1 // BPF_PSEUDO_MAP_FD PseudoMapValue = R2 // BPF_PSEUDO_MAP_VALUE PseudoCall = R1 // BPF_PSEUDO_CALL PseudoFunc = R4 // BPF_PSEUDO_FUNC PseudoKfuncCall = R2 // BPF_PSEUDO_KFUNC_CALL ) func (r Register) String() string { v := uint8(r) if v == 10 { return "rfp" } return fmt.Sprintf("r%d", v) } golang-github-cilium-ebpf-0.21.0+ds1/attachtype_string.go000066400000000000000000000104331520243672000232630ustar00rootroot00000000000000// Code generated by "stringer -type AttachType -trimprefix Attach"; DO NOT EDIT. package ebpf import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[AttachNone-0] _ = x[AttachCGroupInetIngress-0] _ = x[AttachCGroupInetEgress-1] _ = x[AttachCGroupInetSockCreate-2] _ = x[AttachCGroupSockOps-3] _ = x[AttachSkSKBStreamParser-4] _ = x[AttachSkSKBStreamVerdict-5] _ = x[AttachCGroupDevice-6] _ = x[AttachSkMsgVerdict-7] _ = x[AttachCGroupInet4Bind-8] _ = x[AttachCGroupInet6Bind-9] _ = x[AttachCGroupInet4Connect-10] _ = x[AttachCGroupInet6Connect-11] _ = x[AttachCGroupInet4PostBind-12] _ = x[AttachCGroupInet6PostBind-13] _ = x[AttachCGroupUDP4Sendmsg-14] _ = x[AttachCGroupUDP6Sendmsg-15] _ = x[AttachLircMode2-16] _ = x[AttachFlowDissector-17] _ = x[AttachCGroupSysctl-18] _ = x[AttachCGroupUDP4Recvmsg-19] _ = x[AttachCGroupUDP6Recvmsg-20] _ = x[AttachCGroupGetsockopt-21] _ = x[AttachCGroupSetsockopt-22] _ = x[AttachTraceRawTp-23] _ = x[AttachTraceFEntry-24] _ = x[AttachTraceFExit-25] _ = x[AttachModifyReturn-26] _ = x[AttachLSMMac-27] _ = x[AttachTraceIter-28] _ = x[AttachCgroupInet4GetPeername-29] _ = x[AttachCgroupInet6GetPeername-30] _ = x[AttachCgroupInet4GetSockname-31] _ = x[AttachCgroupInet6GetSockname-32] _ = x[AttachXDPDevMap-33] _ = x[AttachCgroupInetSockRelease-34] _ = x[AttachXDPCPUMap-35] _ = x[AttachSkLookup-36] _ = x[AttachXDP-37] _ = x[AttachSkSKBVerdict-38] _ = x[AttachSkReuseportSelect-39] _ = x[AttachSkReuseportSelectOrMigrate-40] _ = x[AttachPerfEvent-41] _ = x[AttachTraceKprobeMulti-42] _ = x[AttachTraceKprobeSession-56] _ = x[AttachLSMCgroup-43] _ = x[AttachStructOps-44] _ = x[AttachNetfilter-45] _ = x[AttachTCXIngress-46] _ = x[AttachTCXEgress-47] _ = x[AttachTraceUprobeMulti-48] _ = x[AttachCgroupUnixConnect-49] _ = x[AttachCgroupUnixSendmsg-50] _ = x[AttachCgroupUnixRecvmsg-51] _ = x[AttachCgroupUnixGetpeername-52] _ = x[AttachCgroupUnixGetsockname-53] _ = x[AttachNetkitPrimary-54] _ = x[AttachNetkitPeer-55] _ = x[AttachWindowsXDP-268435457] _ = x[AttachWindowsBind-268435458] _ = x[AttachWindowsCGroupInet4Connect-268435459] _ = x[AttachWindowsCGroupInet6Connect-268435460] _ = x[AttachWindowsCgroupInet4RecvAccept-268435461] _ = x[AttachWindowsCgroupInet6RecvAccept-268435462] _ = x[AttachWindowsCGroupSockOps-268435463] _ = x[AttachWindowsSample-268435464] _ = x[AttachWindowsXDPTest-268435465] } const ( _AttachType_name_0 = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMultiLSMCgroupStructOpsNetfilterTCXIngressTCXEgressTraceUprobeMultiCgroupUnixConnectCgroupUnixSendmsgCgroupUnixRecvmsgCgroupUnixGetpeernameCgroupUnixGetsocknameNetkitPrimaryNetkitPeerTraceKprobeSession" _AttachType_name_1 = "WindowsXDPWindowsBindWindowsCGroupInet4ConnectWindowsCGroupInet6ConnectWindowsCgroupInet4RecvAcceptWindowsCgroupInet6RecvAcceptWindowsCGroupSockOpsWindowsSampleWindowsXDPTest" ) var ( _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} _AttachType_index_1 = [...]uint8{0, 10, 21, 46, 71, 99, 127, 147, 160, 174} ) func (i AttachType) String() string { switch { case i <= 56: return _AttachType_name_0[_AttachType_index_0[i]:_AttachType_index_0[i+1]] case 268435457 <= i && i <= 268435465: i -= 268435457 return _AttachType_name_1[_AttachType_index_1[i]:_AttachType_index_1[i+1]] default: return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-cilium-ebpf-0.21.0+ds1/btf/000077500000000000000000000000001520243672000177525ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/btf/btf.go000066400000000000000000000322211520243672000210540ustar00rootroot00000000000000package btf import ( "debug/elf" "errors" "fmt" "io" "iter" "maps" "math" "os" "reflect" "slices" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) const btfMagic = 0xeB9F // Errors returned by BTF functions. var ( ErrNotSupported = internal.ErrNotSupported ErrNotFound = errors.New("not found") ErrNoExtendedInfo = errors.New("no extended info") ErrMultipleMatches = errors.New("multiple matching types") ) // ID represents the unique ID of a BTF object. type ID = sys.BTFID type elfData struct { sectionSizes map[string]uint32 symbolOffsets map[elfSymbol]uint32 fixups map[Type]bool } type elfSymbol struct { section string name string } // Spec allows querying a set of Types and loading the set into the // kernel. type Spec struct { *decoder // Additional data from ELF, may be nil. elf *elfData } // LoadSpec opens file and calls LoadSpecFromReader on it. func LoadSpec(file string) (*Spec, error) { fh, err := os.Open(file) if err != nil { return nil, err } defer fh.Close() return LoadSpecFromReader(fh) } // LoadSpecFromReader reads from an ELF or a raw BTF blob. // // Returns ErrNotFound if reading from an ELF which contains no BTF. ExtInfos // may be nil. func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { file, err := internal.NewSafeELFFile(rd) if err != nil { raw, err := io.ReadAll(io.NewSectionReader(rd, 0, math.MaxInt64)) if err != nil { return nil, fmt.Errorf("read raw BTF: %w", err) } return loadRawSpec(raw, nil) } return loadSpecFromELF(file) } // LoadSpecAndExtInfosFromReader reads from an ELF. // // ExtInfos may be nil if the ELF doesn't contain section metadata. // Returns ErrNotFound if the ELF contains no BTF. func LoadSpecAndExtInfosFromReader(rd io.ReaderAt) (*Spec, *ExtInfos, error) { file, err := internal.NewSafeELFFile(rd) if err != nil { return nil, nil, err } spec, err := loadSpecFromELF(file) if err != nil { return nil, nil, err } extInfos, err := loadExtInfosFromELF(file, spec) if err != nil && !errors.Is(err, ErrNotFound) { return nil, nil, err } return spec, extInfos, nil } // symbolOffsets extracts all symbols offsets from an ELF and indexes them by // section and variable name. // // References to variables in BTF data sections carry unsigned 32-bit offsets. // Some ELF symbols (e.g. in vmlinux) may point to virtual memory that is well // beyond this range. Since these symbols cannot be described by BTF info, // ignore them here. func symbolOffsets(file *internal.SafeELFFile) (map[elfSymbol]uint32, error) { symbols, err := file.Symbols() if err != nil { return nil, fmt.Errorf("can't read symbols: %v", err) } offsets := make(map[elfSymbol]uint32) for _, sym := range symbols { if idx := sym.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE { // Ignore things like SHN_ABS continue } if sym.Value > math.MaxUint32 { // VarSecinfo offset is u32, cannot reference symbols in higher regions. continue } if int(sym.Section) >= len(file.Sections) { return nil, fmt.Errorf("symbol %s: invalid section %d", sym.Name, sym.Section) } secName := file.Sections[sym.Section].Name offsets[elfSymbol{secName, sym.Name}] = uint32(sym.Value) } return offsets, nil } func loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) { var ( btfSection *elf.Section sectionSizes = make(map[string]uint32) ) for _, sec := range file.Sections { switch sec.Name { case ".BTF": btfSection = sec default: if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS { break } if sec.Size > math.MaxUint32 { return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) } sectionSizes[sec.Name] = uint32(sec.Size) } } if btfSection == nil { return nil, fmt.Errorf("btf: %w", ErrNotFound) } offsets, err := symbolOffsets(file) if err != nil { return nil, err } rawBTF, err := btfSection.Data() if err != nil { return nil, fmt.Errorf("reading .BTF section: %w", err) } spec, err := loadRawSpec(rawBTF, nil) if err != nil { return nil, err } if spec.decoder.byteOrder != file.ByteOrder { return nil, fmt.Errorf("BTF byte order %s does not match ELF byte order %s", spec.decoder.byteOrder, file.ByteOrder) } spec.elf = &elfData{ sectionSizes, offsets, make(map[Type]bool), } return spec, nil } func loadRawSpec(btf []byte, base *Spec) (*Spec, error) { var ( baseDecoder *decoder baseStrings *stringTable err error ) if base != nil { baseDecoder = base.decoder baseStrings = base.strings } header, bo, err := parseBTFHeader(btf) if err != nil { return nil, fmt.Errorf("parsing .BTF header: %v", err) } if header.HdrLen > uint32(len(btf)) { return nil, fmt.Errorf("BTF header length is out of bounds") } btf = btf[header.HdrLen:] if int(header.StringOff+header.StringLen) > len(btf) { return nil, fmt.Errorf("string table is out of bounds") } stringsSection := btf[header.StringOff : header.StringOff+header.StringLen] rawStrings, err := newStringTable(stringsSection, baseStrings) if err != nil { return nil, fmt.Errorf("read string section: %w", err) } if int(header.TypeOff+header.TypeLen) > len(btf) { return nil, fmt.Errorf("types section is out of bounds") } typesSection := btf[header.TypeOff : header.TypeOff+header.TypeLen] decoder, err := newDecoder(typesSection, bo, rawStrings, baseDecoder) if err != nil { return nil, err } return &Spec{decoder, nil}, nil } // fixupDatasec attempts to patch up missing info in Datasecs and its members by // supplementing them with information from the ELF headers and symbol table. func (elf *elfData) fixupDatasec(typ Type) error { if elf == nil { return nil } if ds, ok := typ.(*Datasec); ok { if elf.fixups[ds] { return nil } elf.fixups[ds] = true name := ds.Name // Some Datasecs are virtual and don't have corresponding ELF sections. switch name { case ".ksyms": // .ksyms describes forward declarations of kfunc signatures, as well as // references to kernel symbols. // Nothing to fix up, all sizes and offsets are 0. for _, vsi := range ds.Vars { switch t := vsi.Type.(type) { case *Func: continue case *Var: if _, ok := t.Type.(*Void); !ok { return fmt.Errorf("data section %s: expected %s to be *Void, not %T: %w", name, vsi.Type.TypeName(), vsi.Type, ErrNotSupported) } default: return fmt.Errorf("data section %s: expected to be either *btf.Func or *btf.Var, not %T: %w", name, vsi.Type, ErrNotSupported) } } return nil case ".kconfig": // .kconfig has a size of 0 and has all members' offsets set to 0. // Fix up all offsets and set the Datasec's size. if err := fixupDatasecLayout(ds); err != nil { return err } // Fix up extern to global linkage to avoid a BTF verifier error. for _, vsi := range ds.Vars { vsi.Type.(*Var).Linkage = GlobalVar } return nil } if ds.Size != 0 { return nil } ds.Size, ok = elf.sectionSizes[name] if !ok { return fmt.Errorf("data section %s: missing size", name) } for i := range ds.Vars { symName := ds.Vars[i].Type.TypeName() ds.Vars[i].Offset, ok = elf.symbolOffsets[elfSymbol{name, symName}] if !ok { return fmt.Errorf("data section %s: missing offset for symbol %s", name, symName) } } } return nil } // fixupDatasecLayout populates ds.Vars[].Offset according to var sizes and // alignment. Calculate and set ds.Size. func fixupDatasecLayout(ds *Datasec) error { var off uint32 for i, vsi := range ds.Vars { v, ok := vsi.Type.(*Var) if !ok { return fmt.Errorf("member %d: unsupported type %T", i, vsi.Type) } size, err := Sizeof(v.Type) if err != nil { return fmt.Errorf("variable %s: getting size: %w", v.Name, err) } align, err := alignof(v.Type) if err != nil { return fmt.Errorf("variable %s: getting alignment: %w", v.Name, err) } // Align the current member based on the offset of the end of the previous // member and the alignment of the current member. off = internal.Align(off, uint32(align)) ds.Vars[i].Offset = off off += uint32(size) } ds.Size = off return nil } // Copy a Spec. // // All contained types are duplicated while preserving any modifications made // to them. func (s *Spec) Copy() *Spec { if s == nil { return nil } cpy := &Spec{ s.decoder.Copy(), nil, } if s.elf != nil { cpy.elf = &elfData{ s.elf.sectionSizes, s.elf.symbolOffsets, maps.Clone(s.elf.fixups), } } return cpy } // TypeByID returns the BTF Type with the given type ID. // // Returns an error wrapping ErrNotFound if a Type with the given ID // does not exist in the Spec. func (s *Spec) TypeByID(id TypeID) (Type, error) { typ, err := s.decoder.TypeByID(id) if err != nil { return nil, fmt.Errorf("inflate type: %w", err) } if err := s.elf.fixupDatasec(typ); err != nil { return nil, err } return typ, nil } // TypeID returns the ID for a given Type. // // Returns an error wrapping [ErrNotFound] if the type isn't part of the Spec. func (s *Spec) TypeID(typ Type) (TypeID, error) { return s.decoder.TypeID(typ) } // AnyTypesByName returns a list of BTF Types with the given name. // // If the BTF blob describes multiple compilation units like vmlinux, multiple // Types with the same name and kind can exist, but might not describe the same // data structure. // // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. func (s *Spec) AnyTypesByName(name string) ([]Type, error) { types, err := s.TypesByName(newEssentialName(name)) if err != nil { return nil, err } for i := 0; i < len(types); i++ { // Match against the full name, not just the essential one // in case the type being looked up is a struct flavor. if types[i].TypeName() != name { types = slices.Delete(types, i, i+1) continue } if err := s.elf.fixupDatasec(types[i]); err != nil { return nil, err } } return types, nil } // AnyTypeByName returns a Type with the given name. // // Returns an error if multiple types of that name exist. func (s *Spec) AnyTypeByName(name string) (Type, error) { types, err := s.AnyTypesByName(name) if err != nil { return nil, err } if len(types) > 1 { return nil, fmt.Errorf("found multiple types: %v", types) } return types[0], nil } // TypeByName searches for a Type with a specific name. Since multiple Types // with the same name can exist, the parameter typ is taken to narrow down the // search in case of a clash. // // typ must be a non-nil pointer to an implementation of a Type. On success, the // address of the found Type will be copied to typ. // // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. // Returns an error wrapping ErrMultipleTypes if multiple candidates are found. func (s *Spec) TypeByName(name string, typ interface{}) error { typeInterface := reflect.TypeOf((*Type)(nil)).Elem() // typ may be **T or *Type typValue := reflect.ValueOf(typ) if typValue.Kind() != reflect.Ptr { return fmt.Errorf("%T is not a pointer", typ) } typPtr := typValue.Elem() if !typPtr.CanSet() { return fmt.Errorf("%T cannot be set", typ) } wanted := typPtr.Type() if wanted == typeInterface { // This is *Type. Unwrap the value's type. wanted = typPtr.Elem().Type() } if !wanted.AssignableTo(typeInterface) { return fmt.Errorf("%T does not satisfy Type interface", typ) } types, err := s.AnyTypesByName(name) if err != nil { return err } var candidate Type for _, typ := range types { if reflect.TypeOf(typ) != wanted { continue } if candidate != nil { return fmt.Errorf("type %s(%T): %w", name, typ, ErrMultipleMatches) } candidate = typ } if candidate == nil { return fmt.Errorf("%s %s: %w", wanted, name, ErrNotFound) } typPtr.Set(reflect.ValueOf(candidate)) return nil } // LoadSplitSpec loads split BTF from the given file. // // Types from base are used to resolve references in the split BTF. // The returned Spec only contains types from the split BTF, not from the base. func LoadSplitSpec(file string, base *Spec) (*Spec, error) { fh, err := os.Open(file) if err != nil { return nil, err } defer fh.Close() return LoadSplitSpecFromReader(fh, base) } // LoadSplitSpecFromReader loads split BTF from a reader. // // Types from base are used to resolve references in the split BTF. // The returned Spec only contains types from the split BTF, not from the base. func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) { raw, err := io.ReadAll(io.NewSectionReader(r, 0, math.MaxInt64)) if err != nil { return nil, fmt.Errorf("read raw BTF: %w", err) } return loadRawSpec(raw, base) } // All iterates over all types. func (s *Spec) All() iter.Seq2[Type, error] { return func(yield func(Type, error) bool) { for id := s.firstTypeID; ; id++ { typ, err := s.TypeByID(id) if errors.Is(err, ErrNotFound) { return } else if err != nil { yield(nil, err) return } // Skip declTags, during unmarshaling declTags become `Tags` fields of other types. // We keep them in the spec to avoid holes in the ID space, but for the purposes of // iteration, they are not useful to the user. if _, ok := typ.(*declTag); ok { continue } if !yield(typ, nil) { return } } } } golang-github-cilium-ebpf-0.21.0+ds1/btf/btf_test.go000066400000000000000000000343521520243672000221220ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "errors" "fmt" "io/fs" "os" "runtime" "sync" "sync/atomic" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func vmlinuxSpec(tb testing.TB) *Spec { tb.Helper() // /sys/kernel/btf was introduced in 341dfcf8d78e ("btf: expose BTF info // through sysfs"), which shipped in Linux 5.4. if _, err := os.Stat("/sys/kernel/btf/vmlinux"); errors.Is(err, fs.ErrNotExist) { tb.Skip("No /sys/kernel/btf/vmlinux") } spec, err := LoadKernelSpec() if err != nil { tb.Fatal(err) } return spec } type specAndRawBTF struct { raw []byte spec *Spec } var vmlinuxTestdata = sync.OnceValues(func() (specAndRawBTF, error) { b, err := internal.ReadAllCompressed("testdata/vmlinux.btf.gz") if err != nil { return specAndRawBTF{}, err } spec, err := loadRawSpec(b, nil) if err != nil { return specAndRawBTF{}, err } return specAndRawBTF{b, spec}, nil }) func vmlinuxTestdataSpec(tb testing.TB) *Spec { tb.Helper() td, err := vmlinuxTestdata() if err != nil { tb.Fatal(err) } return td.spec.Copy() } func vmlinuxTestdataBytes(tb testing.TB) []byte { tb.Helper() td, err := vmlinuxTestdata() if err != nil { tb.Fatal(err) } return td.raw } func parseELFBTF(tb testing.TB, file string) *Spec { tb.Helper() spec, err := LoadSpec(file) if err != nil { tb.Fatal("Can't load BTF:", err) } return spec } func TestAnyTypesByName(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) { spec := parseELFBTF(t, file) types, err := spec.AnyTypesByName("ambiguous") if err != nil { t.Fatal(err) } if len(types) != 1 { t.Fatalf("expected to receive exactly 1 types from querying ambiguous type, got: %v", types) } types, err = spec.AnyTypesByName("ambiguous___flavour") if err != nil { t.Fatal(err) } if len(types) != 1 { t.Fatalf("expected to receive exactly 1 type from querying ambiguous flavour, got: %v", types) } }) } func TestTypeByNameAmbiguous(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) { spec := parseELFBTF(t, file) var typ *Struct if err := spec.TypeByName("ambiguous", &typ); err != nil { t.Fatal(err) } if name := typ.TypeName(); name != "ambiguous" { t.Fatal("expected type name 'ambiguous', got:", name) } if err := spec.TypeByName("ambiguous___flavour", &typ); err != nil { t.Fatal(err) } if name := typ.TypeName(); name != "ambiguous___flavour" { t.Fatal("expected type name 'ambiguous___flavour', got:", name) } }) } func TestTypeByName(t *testing.T) { spec := vmlinuxTestdataSpec(t) for _, typ := range []interface{}{ nil, Struct{}, &Struct{}, []Struct{}, &[]Struct{}, map[int]Struct{}, &map[int]Struct{}, int(0), new(int), } { t.Run(fmt.Sprintf("%T", typ), func(t *testing.T) { // spec.TypeByName MUST fail if typ is a nil btf.Type. if err := spec.TypeByName("iphdr", typ); err == nil { t.Fatalf("TypeByName does not fail with type %T", typ) } }) } // spec.TypeByName MUST return the same address for multiple calls with the same type name. var iphdr1, iphdr2 *Struct if err := spec.TypeByName("iphdr", &iphdr1); err != nil { t.Fatal(err) } if err := spec.TypeByName("iphdr", &iphdr2); err != nil { t.Fatal(err) } if iphdr1 != iphdr2 { t.Fatal("multiple TypeByName calls for `iphdr` name do not return the same addresses") } // It's valid to pass a *Type to TypeByName. typ := Type(iphdr2) if err := spec.TypeByName("iphdr", &typ); err != nil { t.Fatal("Can't look up using *Type:", err) } // Excerpt from linux/ip.h, https://elixir.bootlin.com/linux/latest/A/ident/iphdr // // struct iphdr { // #if defined(__LITTLE_ENDIAN_BITFIELD) // __u8 ihl:4, version:4; // #elif defined (__BIG_ENDIAN_BITFIELD) // __u8 version:4, ihl:4; // #else // ... // } // // The BTF we test against is for little endian. m := iphdr1.Members[1] if m.Name != "version" { t.Fatal("Expected version as the second member, got", m.Name) } td, ok := m.Type.(*Typedef) if !ok { t.Fatalf("version member of iphdr should be a __u8 typedef: actual: %T", m.Type) } u8, ok := td.Type.(*Int) if !ok { t.Fatalf("__u8 typedef should point to an Int type: actual: %T", td.Type) } if m.BitfieldSize != 4 { t.Fatalf("incorrect bitfield size: expected: 4 actual: %d", m.BitfieldSize) } if u8.Encoding != 0 { t.Fatalf("incorrect encoding of an __u8 int: expected: 0 actual: %x", u8.Encoding) } if m.Offset != 4 { t.Fatalf("incorrect bitfield offset: expected: 4 actual: %d", m.Offset) } } func BenchmarkParseVmlinux(b *testing.B) { vmlinux := vmlinuxTestdataBytes(b) b.ReportAllocs() for b.Loop() { if _, err := loadRawSpec(vmlinux, nil); err != nil { b.Fatal("Can't load BTF:", err) } } } func BenchmarkIterateVmlinux(b *testing.B) { vmlinux := vmlinuxTestdataBytes(b) b.ReportAllocs() for b.Loop() { spec, err := loadRawSpec(vmlinux, nil) if err != nil { b.Fatal("Can't load BTF:", err) } for range spec.All() { } } } func TestParseCurrentKernelBTF(t *testing.T) { spec := vmlinuxSpec(t) if len(spec.offsets) == 0 { t.Fatal("Empty kernel BTF") } } func TestFindVMLinux(t *testing.T) { file, err := findVMLinux() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't find vmlinux:", err) } defer file.Close() spec, err := LoadSpecFromReader(file) if err != nil { t.Fatal("Can't load BTF:", err) } if len(spec.offsets) == 0 { t.Fatal("Empty kernel BTF") } } func TestLoadSpecFromElf(t *testing.T) { testutils.Files(t, testutils.Glob(t, "../testdata/loader-e*.elf"), func(t *testing.T, file string) { spec := parseELFBTF(t, file) vt, err := spec.TypeByID(0) if err != nil { t.Error("Can't retrieve void type by ID:", err) } if _, ok := vt.(*Void); !ok { t.Errorf("Expected Void for type id 0, but got: %T", vt) } var bpfMapDef *Struct if err := spec.TypeByName("bpf_map_def", &bpfMapDef); err != nil { t.Error("Can't find bpf_map_def:", err) } var tmp *Void if err := spec.TypeByName("totally_bogus_type", &tmp); !errors.Is(err, ErrNotFound) { t.Error("TypeByName doesn't return ErrNotFound:", err) } var fn *Func if err := spec.TypeByName("global_fn", &fn); err != nil { t.Error("Can't find global_fn():", err) } else { if fn.Linkage != GlobalFunc { t.Error("Expected global linkage:", fn) } } var v *Var if err := spec.TypeByName("key3", &v); err != nil { t.Error("Can't find key3:", err) } else { if v.Linkage != GlobalVar { t.Error("Expected global linkage:", v) } } }) } func TestVerifierError(t *testing.T) { b, err := NewBuilder([]Type{&Int{Encoding: 255}}, nil) qt.Assert(t, qt.IsNil(err)) _, err = NewHandle(b) testutils.SkipIfNotSupported(t, err) var ve *internal.VerifierError if !errors.As(err, &ve) { t.Fatalf("expected a VerifierError, got: %v", err) } } func TestSpecCopy(t *testing.T) { qt.Check(t, qt.IsNil((*Spec)(nil).Copy())) spec := parseELFBTF(t, "../testdata/loader-el.elf") cpy := spec.Copy() have := typesFromSpec(t, spec) qt.Assert(t, qt.IsTrue(len(have) > 0)) want := typesFromSpec(t, cpy) qt.Assert(t, qt.HasLen(want, len(have))) for i := range want { if _, ok := have[i].(*Void); ok { // Since Void is an empty struct, a Type interface value containing // &Void{} stores (*Void, nil). Since interface equality first compares // the type and then the concrete value, Void is always equal. continue } if have[i] == want[i] { t.Fatalf("Type at index %d is not a copy: %T == %T", i, have[i], want[i]) } } } func TestSpecCopyModifications(t *testing.T) { spec := specFromTypes(t, []Type{&Int{Name: "a", Size: 4}}) typ, err := spec.TypeByID(1) qt.Assert(t, qt.IsNil(err)) i := typ.(*Int) i.Name = "b" i.Size = 2 cpy := spec.Copy() typ2, err := cpy.TypeByID(1) qt.Assert(t, qt.IsNil(err)) i2 := typ2.(*Int) qt.Assert(t, qt.Not(qt.Equals(i2, i)), qt.Commentf("Types are distinct")) qt.Assert(t, qt.DeepEquals(i2, i), qt.Commentf("Modifications are preserved")) i.Name = "bar" qt.Assert(t, qt.Equals(i2.Name, "b")) } func TestSpecTypeByID(t *testing.T) { spec := specFromTypes(t, nil) _, err := spec.TypeByID(0) qt.Assert(t, qt.IsNil(err)) _, err = spec.TypeByID(1) qt.Assert(t, qt.ErrorIs(err, ErrNotFound)) } func ExampleSpec_TypeByName() { // Acquire a Spec via one of its constructors. spec := new(Spec) // Declare a variable of the desired type var foo *Struct if err := spec.TypeByName("foo", &foo); err != nil { // There is no struct with name foo, or there // are multiple possibilities. } // We've found struct foo fmt.Println(foo.Name) } func TestTypesIterator(t *testing.T) { types := []Type{(*Void)(nil), &Int{Size: 4}, &Int{Size: 2}} b, err := NewBuilder(types[1:], nil) if err != nil { t.Fatal(err) } raw, err := b.Marshal(nil, nil) if err != nil { t.Fatal(err) } spec, err := LoadSpecFromReader(bytes.NewReader(raw)) if err != nil { t.Fatal(err) } var have []Type for typ, err := range spec.All() { qt.Assert(t, qt.IsNil(err)) have = append(have, typ) } qt.Assert(t, qt.DeepEquals(have, types)) } func TestLoadSplitSpec(t *testing.T) { spec, err := LoadSpec("testdata/btf_testmod.btf.base") if err != nil { t.Fatal(err) } splitSpec, err := LoadSplitSpec("testdata/btf_testmod.btf", spec) if err != nil { t.Fatal(err) } var fnType *Func qt.Assert(t, qt.IsNil(splitSpec.TypeByName("bpf_testmod_init", &fnType))) typeID, err := splitSpec.TypeID(fnType) qt.Assert(t, qt.IsNil(err)) typeByID, err := splitSpec.TypeByID(typeID) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(typeByID, Type(fnType))) fnProto := fnType.Type.(*FuncProto) // 'int' is defined in the base BTF... intType, err := spec.AnyTypeByName("int") qt.Assert(t, qt.IsNil(err)) // ... but not in the split BTF _, err = splitSpec.AnyTypeByName("int") qt.Assert(t, qt.ErrorIs(err, ErrNotFound)) qt.Assert(t, qt.Equals(fnProto.Return, intType), qt.Commentf("types found in base of split spec should be reused")) fnProto.Params = []FuncParam{{"a", &Pointer{(*Void)(nil)}}} // The behaviour of copying a split spec is quite subtle. When initially // creating a split spec, types in the split base are shared. This allows // amortising the cost of decoding vmlinux. // // However, we currently define copying a spec to be like forking a process: // in-memory changes to types are preserved. After the copy finished we have // two fully independent states. // // For split BTF this means that we also need to copy the base and ensure // that future references to a modified type work correctly. splitSpecCopy := splitSpec.Copy() var fnCopyType *Func qt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName("bpf_testmod_init", &fnCopyType))) qt.Assert(t, testutils.IsDeepCopy(fnCopyType, fnType)) // Pull out a second type which refers to "int" in the base, but which hasn't // been inflated yet. This forces inflating int from the base. var str *Struct qt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName("bpf_testmod_struct_arg_1", &str))) // Ensure that the int types are indeed the same. qt.Assert(t, qt.Equals(str.Members[0].Type, fnCopyType.Type.(*FuncProto).Return)) copyTypeID, err := splitSpecCopy.TypeID(fnCopyType) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(copyTypeID, typeID), qt.Commentf("ID of copied type must match")) } func TestFixupDatasecLayout(t *testing.T) { ds := &Datasec{ Size: 0, // Populated by fixup. Vars: []VarSecinfo{ {Type: &Var{Type: &Int{Size: 4}}}, {Type: &Var{Type: &Int{Size: 1}}}, {Type: &Var{Type: &Int{Size: 1}}}, {Type: &Var{Type: &Int{Size: 2}}}, {Type: &Var{Type: &Int{Size: 16}}}, {Type: &Var{Type: &Int{Size: 8}}}, }, } qt.Assert(t, qt.IsNil(fixupDatasecLayout(ds))) qt.Assert(t, qt.Equals(ds.Size, 40)) qt.Assert(t, qt.Equals(ds.Vars[0].Offset, 0)) qt.Assert(t, qt.Equals(ds.Vars[1].Offset, 4)) qt.Assert(t, qt.Equals(ds.Vars[2].Offset, 5)) qt.Assert(t, qt.Equals(ds.Vars[3].Offset, 6)) qt.Assert(t, qt.Equals(ds.Vars[4].Offset, 16)) qt.Assert(t, qt.Equals(ds.Vars[5].Offset, 32)) } func TestSpecConcurrentAccess(t *testing.T) { spec := vmlinuxTestdataSpec(t) maxprocs := runtime.GOMAXPROCS(0) if maxprocs < 2 { t.Error("GOMAXPROCS is lower than 2:", maxprocs) } var cond atomic.Int64 var wg sync.WaitGroup for i := 0; i < maxprocs; i++ { wg.Add(1) go func() { defer wg.Done() n := cond.Add(1) for cond.Load() != int64(maxprocs) { // Spin to increase the chances of a race. } if n%2 == 0 { _, _ = spec.AnyTypeByName("gov_update_cpu_data") } else { _ = spec.Copy() } }() // Try to get the Goroutines scheduled and spinning. runtime.Gosched() } wg.Wait() } func TestLoadEmptyRawSpec(t *testing.T) { buf, err := binary.Append(nil, binary.LittleEndian, &btfHeader{ Magic: btfMagic, Version: 1, Flags: 0, HdrLen: uint32(btfHeaderLen), TypeOff: 0, TypeLen: 0, StringOff: 0, StringLen: 0, }) qt.Assert(t, qt.IsNil(err)) _, err = loadRawSpec(buf, nil) qt.Assert(t, qt.IsNil(err)) } func BenchmarkSpecCopy(b *testing.B) { spec := vmlinuxTestdataSpec(b) for b.Loop() { spec.Copy() } } func BenchmarkSpecTypeByID(b *testing.B) { spec := vmlinuxTestdataSpec(b) b.ReportAllocs() for b.Loop() { _, err := spec.TypeByID(1) if err != nil { b.Fatal(err) } } } func BenchmarkInspektorGadget(b *testing.B) { // This benchmark is the baseline for what Inspektor Gadget loads for a // common configuration. types := []string{ "pt_regs", "file", "inode", "super_block", "socket", "syscall_trace_enter", "task_struct", "nsproxy", "mnt_namespace", // "fanotify_event", "pid", "trace_event_raw_sched_process_exec", "fs_struct", "path", "mount", "qstr", "vfsmount", "dentry", // "bpf_func_id", "mm_struct", "syscall_trace_exit", "linux_binprm", "sock", "net", "inet_sock", } vmlinux, err := internal.ReadAllCompressed("testdata/vmlinux.btf.gz") qt.Assert(b, qt.IsNil(err)) var rd bytes.Reader for b.Loop() { rd.Reset(vmlinux) spec, err := LoadSpecFromReader(&rd) if err != nil { b.Fatal(err) } var s *Struct for _, name := range types { if err := spec.TypeByName(name, &s); err != nil { b.Fatal(name, err) } } } } golang-github-cilium-ebpf-0.21.0+ds1/btf/btf_types.go000066400000000000000000000276611520243672000223140ustar00rootroot00000000000000package btf import ( "encoding/binary" "errors" "fmt" "unsafe" ) //go:generate go tool stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind // btfKind describes a Type. type btfKind uint8 // Equivalents of the BTF_KIND_* constants. const ( kindUnknown btfKind = iota // Unknown kindInt // Int kindPointer // Pointer kindArray // Array kindStruct // Struct kindUnion // Union kindEnum // Enum kindForward // Forward kindTypedef // Typedef kindVolatile // Volatile kindConst // Const kindRestrict // Restrict // Added ~4.20 kindFunc // Func kindFuncProto // FuncProto // Added ~5.1 kindVar // Var kindDatasec // Datasec // Added ~5.13 kindFloat // Float // Added 5.16 kindDeclTag // DeclTag // Added 5.17 kindTypeTag // TypeTag // Added 6.0 kindEnum64 // Enum64 ) // FuncLinkage describes BTF function linkage metadata. type FuncLinkage int // Equivalent of enum btf_func_linkage. const ( StaticFunc FuncLinkage = iota // static GlobalFunc // global ExternFunc // extern ) // VarLinkage describes BTF variable linkage metadata. type VarLinkage int const ( StaticVar VarLinkage = iota // static GlobalVar // global ExternVar // extern ) const ( btfTypeKindShift = 24 btfTypeKindLen = 5 btfTypeVlenShift = 0 btfTypeVlenMask = 16 btfTypeKindFlagShift = 31 btfTypeKindFlagMask = 1 ) var btfHeaderLen = binary.Size(&btfHeader{}) type btfHeader struct { Magic uint16 Version uint8 Flags uint8 HdrLen uint32 TypeOff uint32 TypeLen uint32 StringOff uint32 StringLen uint32 } // parseBTFHeader parses the header of the .BTF section. func parseBTFHeader(buf []byte) (*btfHeader, binary.ByteOrder, error) { var header btfHeader var bo binary.ByteOrder for _, order := range []binary.ByteOrder{binary.LittleEndian, binary.BigEndian} { n, err := binary.Decode(buf, order, &header) if err != nil { return nil, nil, fmt.Errorf("read header: %v", err) } if header.Magic != btfMagic { continue } buf = buf[n:] bo = order break } if bo == nil { return nil, nil, fmt.Errorf("no valid BTF header") } if header.Version != 1 { return nil, nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) } remainder := int64(header.HdrLen) - int64(binary.Size(&header)) if remainder < 0 { return nil, nil, errors.New("header length shorter than btfHeader size") } for _, b := range buf[:remainder] { if b != 0 { return nil, nil, errors.New("header contains non-zero trailer") } } return &header, bo, nil } // btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst. type btfType struct { NameOff uint32 /* "info" bits arrangement * bits 0-15: vlen (e.g. # of struct's members), linkage * bits 16-23: unused * bits 24-28: kind (e.g. int, ptr, array...etc) * bits 29-30: unused * bit 31: kind_flag, currently used by * struct, union and fwd */ Info uint32 /* "size" is used by INT, ENUM, STRUCT and UNION. * "size" tells the size of the type it is describing. * * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, * FUNC and FUNC_PROTO. * "type" is a type_id referring to another type. */ SizeType uint32 } var btfTypeSize = int(unsafe.Sizeof(btfType{})) func unmarshalBtfType(bt *btfType, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfTypeSize { return 0, fmt.Errorf("not enough bytes to unmarshal btfType") } bt.NameOff = bo.Uint32(b[0:]) bt.Info = bo.Uint32(b[4:]) bt.SizeType = bo.Uint32(b[8:]) return btfTypeSize, nil } func mask(len uint32) uint32 { return (1 << len) - 1 } func readBits(value, len, shift uint32) uint32 { return (value >> shift) & mask(len) } func writeBits(value, len, shift, new uint32) uint32 { value &^= mask(len) << shift value |= (new & mask(len)) << shift return value } func (bt *btfType) info(len, shift uint32) uint32 { return readBits(bt.Info, len, shift) } func (bt *btfType) setInfo(value, len, shift uint32) { bt.Info = writeBits(bt.Info, len, shift, value) } func (bt *btfType) Kind() btfKind { return btfKind(bt.info(btfTypeKindLen, btfTypeKindShift)) } func (bt *btfType) SetKind(kind btfKind) { bt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift) } func (bt *btfType) Vlen() int { return int(bt.info(btfTypeVlenMask, btfTypeVlenShift)) } func (bt *btfType) SetVlen(vlen int) { bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift) } func (bt *btfType) kindFlagBool() bool { return bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift) == 1 } func (bt *btfType) setKindFlagBool(set bool) { var value uint32 if set { value = 1 } bt.setInfo(value, btfTypeKindFlagMask, btfTypeKindFlagShift) } // Bitfield returns true if the struct or union contain a bitfield. func (bt *btfType) Bitfield() bool { return bt.kindFlagBool() } func (bt *btfType) SetBitfield(isBitfield bool) { bt.setKindFlagBool(isBitfield) } func (bt *btfType) FwdKind() FwdKind { return FwdKind(bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift)) } func (bt *btfType) SetFwdKind(kind FwdKind) { bt.setInfo(uint32(kind), btfTypeKindFlagMask, btfTypeKindFlagShift) } func (bt *btfType) Signed() bool { return bt.kindFlagBool() } func (bt *btfType) SetSigned(signed bool) { bt.setKindFlagBool(signed) } func (bt *btfType) Linkage() FuncLinkage { return FuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift)) } func (bt *btfType) SetLinkage(linkage FuncLinkage) { bt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift) } func (bt *btfType) Type() TypeID { // TODO: Panic here if wrong kind? return TypeID(bt.SizeType) } func (bt *btfType) SetType(id TypeID) { bt.SizeType = uint32(id) } func (bt *btfType) Size() uint32 { // TODO: Panic here if wrong kind? return bt.SizeType } func (bt *btfType) SetSize(size uint32) { bt.SizeType = size } func (bt *btfType) Encode(buf []byte, bo binary.ByteOrder) (int, error) { if len(buf) < btfTypeSize { return 0, fmt.Errorf("not enough bytes to marshal btfType") } bo.PutUint32(buf[0:], bt.NameOff) bo.PutUint32(buf[4:], bt.Info) bo.PutUint32(buf[8:], bt.SizeType) return btfTypeSize, nil } // DataLen returns the length of additional type specific data in bytes. func (bt *btfType) DataLen() (int, error) { switch bt.Kind() { case kindInt: return int(unsafe.Sizeof(btfInt{})), nil case kindPointer: case kindArray: return int(unsafe.Sizeof(btfArray{})), nil case kindStruct: fallthrough case kindUnion: return int(unsafe.Sizeof(btfMember{})) * bt.Vlen(), nil case kindEnum: return int(unsafe.Sizeof(btfEnum{})) * bt.Vlen(), nil case kindForward: case kindTypedef: case kindVolatile: case kindConst: case kindRestrict: case kindFunc: case kindFuncProto: return int(unsafe.Sizeof(btfParam{})) * bt.Vlen(), nil case kindVar: return int(unsafe.Sizeof(btfVariable{})), nil case kindDatasec: return int(unsafe.Sizeof(btfVarSecinfo{})) * bt.Vlen(), nil case kindFloat: case kindDeclTag: return int(unsafe.Sizeof(btfDeclTag{})), nil case kindTypeTag: case kindEnum64: return int(unsafe.Sizeof(btfEnum64{})) * bt.Vlen(), nil default: return 0, fmt.Errorf("unknown kind: %v", bt.Kind()) } return 0, nil } // btfInt encodes additional data for integers. // // ? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b // ? = undefined // e = encoding // o = offset (bitfields?) // b = bits (bitfields) type btfInt struct { Raw uint32 } const ( btfIntEncodingLen = 4 btfIntEncodingShift = 24 btfIntOffsetLen = 8 btfIntOffsetShift = 16 btfIntBitsLen = 8 btfIntBitsShift = 0 ) var btfIntLen = int(unsafe.Sizeof(btfInt{})) func unmarshalBtfInt(bi *btfInt, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfIntLen { return 0, fmt.Errorf("not enough bytes to unmarshal btfInt") } bi.Raw = bo.Uint32(b[0:]) return btfIntLen, nil } func (bi btfInt) Encoding() IntEncoding { return IntEncoding(readBits(bi.Raw, btfIntEncodingLen, btfIntEncodingShift)) } func (bi *btfInt) SetEncoding(e IntEncoding) { bi.Raw = writeBits(uint32(bi.Raw), btfIntEncodingLen, btfIntEncodingShift, uint32(e)) } func (bi btfInt) Offset() Bits { return Bits(readBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift)) } func (bi *btfInt) SetOffset(offset uint32) { bi.Raw = writeBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift, offset) } func (bi btfInt) Bits() Bits { return Bits(readBits(bi.Raw, btfIntBitsLen, btfIntBitsShift)) } func (bi *btfInt) SetBits(bits byte) { bi.Raw = writeBits(bi.Raw, btfIntBitsLen, btfIntBitsShift, uint32(bits)) } type btfArray struct { Type TypeID IndexType TypeID Nelems uint32 } var btfArrayLen = int(unsafe.Sizeof(btfArray{})) func unmarshalBtfArray(ba *btfArray, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfArrayLen { return 0, fmt.Errorf("not enough bytes to unmarshal btfArray") } ba.Type = TypeID(bo.Uint32(b[0:])) ba.IndexType = TypeID(bo.Uint32(b[4:])) ba.Nelems = bo.Uint32(b[8:]) return btfArrayLen, nil } type btfMember struct { NameOff uint32 Type TypeID Offset uint32 } var btfMemberLen = int(unsafe.Sizeof(btfMember{})) func unmarshalBtfMember(bm *btfMember, b []byte, bo binary.ByteOrder) (int, error) { if btfMemberLen > len(b) { return 0, fmt.Errorf("not enough bytes to unmarshal btfMember") } bm.NameOff = bo.Uint32(b[0:]) bm.Type = TypeID(bo.Uint32(b[4:])) bm.Offset = bo.Uint32(b[8:]) return btfMemberLen, nil } type btfVarSecinfo struct { Type TypeID Offset uint32 Size uint32 } var btfVarSecinfoLen = int(unsafe.Sizeof(btfVarSecinfo{})) func unmarshalBtfVarSecInfo(bvsi *btfVarSecinfo, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfVarSecinfoLen { return 0, fmt.Errorf("not enough bytes to unmarshal btfVarSecinfo") } bvsi.Type = TypeID(bo.Uint32(b[0:])) bvsi.Offset = bo.Uint32(b[4:]) bvsi.Size = bo.Uint32(b[8:]) return btfVarSecinfoLen, nil } type btfVariable struct { Linkage uint32 } var btfVariableLen = int(unsafe.Sizeof(btfVariable{})) func unmarshalBtfVariable(bv *btfVariable, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfVariableLen { return 0, fmt.Errorf("not enough bytes to unmarshal btfVariable") } bv.Linkage = bo.Uint32(b[0:]) return btfVariableLen, nil } type btfEnum struct { NameOff uint32 Val uint32 } var btfEnumLen = int(unsafe.Sizeof(btfEnum{})) func unmarshalBtfEnum(be *btfEnum, b []byte, bo binary.ByteOrder) (int, error) { if btfEnumLen > len(b) { return 0, fmt.Errorf("not enough bytes to unmarshal btfEnum") } be.NameOff = bo.Uint32(b[0:]) be.Val = bo.Uint32(b[4:]) return btfEnumLen, nil } type btfEnum64 struct { NameOff uint32 ValLo32 uint32 ValHi32 uint32 } var btfEnum64Len = int(unsafe.Sizeof(btfEnum64{})) func unmarshalBtfEnum64(enum *btfEnum64, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfEnum64Len { return 0, fmt.Errorf("not enough bytes to unmarshal btfEnum64") } enum.NameOff = bo.Uint32(b[0:]) enum.ValLo32 = bo.Uint32(b[4:]) enum.ValHi32 = bo.Uint32(b[8:]) return btfEnum64Len, nil } type btfParam struct { NameOff uint32 Type TypeID } var btfParamLen = int(unsafe.Sizeof(btfParam{})) func unmarshalBtfParam(param *btfParam, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfParamLen { return 0, fmt.Errorf("not enough bytes to unmarshal btfParam") } param.NameOff = bo.Uint32(b[0:]) param.Type = TypeID(bo.Uint32(b[4:])) return btfParamLen, nil } type btfDeclTag struct { ComponentIdx uint32 } var btfDeclTagLen = int(unsafe.Sizeof(btfDeclTag{})) func unmarshalBtfDeclTag(bdt *btfDeclTag, b []byte, bo binary.ByteOrder) (int, error) { if len(b) < btfDeclTagLen { return 0, fmt.Errorf("not enough bytes to unmarshal btfDeclTag") } bdt.ComponentIdx = bo.Uint32(b[0:]) return btfDeclTagLen, nil } golang-github-cilium-ebpf-0.21.0+ds1/btf/btf_types_string.go000066400000000000000000000046121520243672000236710ustar00rootroot00000000000000// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind"; DO NOT EDIT. package btf import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[StaticFunc-0] _ = x[GlobalFunc-1] _ = x[ExternFunc-2] } const _FuncLinkage_name = "staticglobalextern" var _FuncLinkage_index = [...]uint8{0, 6, 12, 18} func (i FuncLinkage) String() string { idx := int(i) - 0 if i < 0 || idx >= len(_FuncLinkage_index)-1 { return "FuncLinkage(" + strconv.FormatInt(int64(i), 10) + ")" } return _FuncLinkage_name[_FuncLinkage_index[idx]:_FuncLinkage_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[StaticVar-0] _ = x[GlobalVar-1] _ = x[ExternVar-2] } const _VarLinkage_name = "staticglobalextern" var _VarLinkage_index = [...]uint8{0, 6, 12, 18} func (i VarLinkage) String() string { idx := int(i) - 0 if i < 0 || idx >= len(_VarLinkage_index)-1 { return "VarLinkage(" + strconv.FormatInt(int64(i), 10) + ")" } return _VarLinkage_name[_VarLinkage_index[idx]:_VarLinkage_index[idx+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[kindUnknown-0] _ = x[kindInt-1] _ = x[kindPointer-2] _ = x[kindArray-3] _ = x[kindStruct-4] _ = x[kindUnion-5] _ = x[kindEnum-6] _ = x[kindForward-7] _ = x[kindTypedef-8] _ = x[kindVolatile-9] _ = x[kindConst-10] _ = x[kindRestrict-11] _ = x[kindFunc-12] _ = x[kindFuncProto-13] _ = x[kindVar-14] _ = x[kindDatasec-15] _ = x[kindFloat-16] _ = x[kindDeclTag-17] _ = x[kindTypeTag-18] _ = x[kindEnum64-19] } const _btfKind_name = "UnknownIntPointerArrayStructUnionEnumForwardTypedefVolatileConstRestrictFuncFuncProtoVarDatasecFloatDeclTagTypeTagEnum64" var _btfKind_index = [...]uint8{0, 7, 10, 17, 22, 28, 33, 37, 44, 51, 59, 64, 72, 76, 85, 88, 95, 100, 107, 114, 120} func (i btfKind) String() string { idx := int(i) - 0 if i < 0 || idx >= len(_btfKind_index)-1 { return "btfKind(" + strconv.FormatInt(int64(i), 10) + ")" } return _btfKind_name[_btfKind_index[idx]:_btfKind_index[idx+1]] } golang-github-cilium-ebpf-0.21.0+ds1/btf/core.go000066400000000000000000001120711520243672000212330ustar00rootroot00000000000000package btf import ( "encoding/binary" "errors" "fmt" "math" "reflect" "strconv" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" ) // Code in this file is derived from libbpf, which is available under a BSD // 2-Clause license. // A constant used when CO-RE relocation has to remove instructions. // // Taken from libbpf. const COREBadRelocationSentinel = 0xbad2310 // COREFixup is the result of computing a CO-RE relocation for a target. type COREFixup struct { kind coreKind local uint64 target uint64 // True if there is no valid fixup. The instruction is replaced with an // invalid dummy. poison bool // True if the validation of the local value should be skipped. Used by // some kinds of bitfield relocations. skipLocalValidation bool } func (f *COREFixup) equal(other COREFixup) bool { return f.local == other.local && f.target == other.target } func (f *COREFixup) String() string { if f.poison { return fmt.Sprintf("%s=poison", f.kind) } return fmt.Sprintf("%s=%d->%d", f.kind, f.local, f.target) } func (f *COREFixup) Apply(ins *asm.Instruction) error { if !platform.IsLinux { return fmt.Errorf("CO-RE fixup: %w", internal.ErrNotSupportedOnOS) } if f.poison { // Relocation is poisoned, replace the instruction with an invalid one. if ins.OpCode.IsDWordLoad() { // Replace a dword load with a invalid dword load to preserve instruction size. *ins = asm.LoadImm(asm.R10, COREBadRelocationSentinel, asm.DWord) } else { // Replace all single size instruction with a invalid call instruction. *ins = asm.BuiltinFunc(COREBadRelocationSentinel).Call() } // Add context to the kernel verifier output. if source := ins.Source(); source != nil { *ins = ins.WithSource(asm.Comment(fmt.Sprintf("instruction poisoned by CO-RE: %s", source))) } else { *ins = ins.WithSource(asm.Comment("instruction poisoned by CO-RE")) } return nil } switch class := ins.OpCode.Class(); class { case asm.LdXClass, asm.StClass, asm.StXClass: if want := int16(f.local); !f.skipLocalValidation && want != ins.Offset { return fmt.Errorf("invalid offset %d, expected %d", ins.Offset, f.local) } if f.target > math.MaxInt16 { return fmt.Errorf("offset %d exceeds MaxInt16", f.target) } ins.Offset = int16(f.target) case asm.LdClass: if !ins.IsConstantLoad(asm.DWord) { return fmt.Errorf("not a dword-sized immediate load") } if want := int64(f.local); !f.skipLocalValidation && want != ins.Constant { return fmt.Errorf("invalid immediate %d, expected %d (fixup: %v)", ins.Constant, want, f) } ins.Constant = int64(f.target) case asm.ALUClass: if ins.OpCode.ALUOp() == asm.Swap { return fmt.Errorf("relocation against swap") } fallthrough case asm.ALU64Class: if src := ins.OpCode.Source(); src != asm.ImmSource { return fmt.Errorf("invalid source %s", src) } if want := int64(f.local); !f.skipLocalValidation && want != ins.Constant { return fmt.Errorf("invalid immediate %d, expected %d (fixup: %v, kind: %v, ins: %v)", ins.Constant, want, f, f.kind, ins) } if f.target > math.MaxInt32 { return fmt.Errorf("immediate %d exceeds MaxInt32", f.target) } ins.Constant = int64(f.target) default: return fmt.Errorf("invalid class %s", class) } return nil } func (f COREFixup) isNonExistant() bool { return f.kind.checksForExistence() && f.target == 0 } // coreKind is the type of CO-RE relocation as specified in BPF source code. type coreKind uint32 const ( reloFieldByteOffset coreKind = iota /* field byte offset */ reloFieldByteSize /* field size in bytes */ reloFieldExists /* field existence in target kernel */ reloFieldSigned /* field signedness (0 - unsigned, 1 - signed) */ reloFieldLShiftU64 /* bitfield-specific left bitshift */ reloFieldRShiftU64 /* bitfield-specific right bitshift */ reloTypeIDLocal /* type ID in local BPF object */ reloTypeIDTarget /* type ID in target kernel */ reloTypeExists /* type existence in target kernel */ reloTypeSize /* type size in bytes */ reloEnumvalExists /* enum value existence in target kernel */ reloEnumvalValue /* enum value integer value */ reloTypeMatches /* type matches kernel type */ ) func (k coreKind) checksForExistence() bool { return k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists || k == reloTypeMatches } func (k coreKind) String() string { switch k { case reloFieldByteOffset: return "byte_off" case reloFieldByteSize: return "byte_sz" case reloFieldExists: return "field_exists" case reloFieldSigned: return "signed" case reloFieldLShiftU64: return "lshift_u64" case reloFieldRShiftU64: return "rshift_u64" case reloTypeIDLocal: return "local_type_id" case reloTypeIDTarget: return "target_type_id" case reloTypeExists: return "type_exists" case reloTypeSize: return "type_size" case reloEnumvalExists: return "enumval_exists" case reloEnumvalValue: return "enumval_value" case reloTypeMatches: return "type_matches" default: return fmt.Sprintf("unknown (%d)", k) } } // CORERelocate calculates changes needed to adjust eBPF instructions for differences // in types. // // targets forms the set of types to relocate against. The first element has to be // BTF for vmlinux, the following must be types for kernel modules. // // resolveLocalTypeID is called for each local type which requires a stable TypeID. // Calling the function with the same type multiple times must produce the same // result. It is the callers responsibility to ensure that the relocated instructions // are loaded with matching BTF. // // Returns a list of fixups which can be applied to instructions to make them // match the target type(s). // // Fixups are returned in the order of relos, e.g. fixup[i] is the solution // for relos[i]. func CORERelocate(relos []*CORERelocation, targets []*Spec, bo binary.ByteOrder, resolveLocalTypeID func(Type) (TypeID, error)) ([]COREFixup, error) { if len(targets) == 0 { // Explicitly check for nil here since the argument used to be optional. return nil, fmt.Errorf("targets must be provided") } // We can't encode type IDs that aren't for vmlinux into instructions at the // moment. resolveTargetTypeID := targets[0].TypeID for _, target := range targets { if bo != target.byteOrder { return nil, fmt.Errorf("can't relocate %s against %s", bo, target.byteOrder) } } type reloGroup struct { relos []*CORERelocation // Position of each relocation in relos. indices []int } // Split relocations into per Type lists. relosByType := make(map[Type]*reloGroup) result := make([]COREFixup, len(relos)) for i, relo := range relos { if relo.kind == reloTypeIDLocal { // Filtering out reloTypeIDLocal here makes our lives a lot easier // down the line, since it doesn't have a target at all. if len(relo.accessor) > 1 || relo.accessor[0] != 0 { return nil, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor) } id, err := resolveLocalTypeID(relo.typ) if err != nil { return nil, fmt.Errorf("%s: get type id: %w", relo.kind, err) } result[i] = COREFixup{ kind: relo.kind, local: uint64(relo.id), target: uint64(id), } continue } group, ok := relosByType[relo.typ] if !ok { group = &reloGroup{} relosByType[relo.typ] = group } group.relos = append(group.relos, relo) group.indices = append(group.indices, i) } for localType, group := range relosByType { localTypeName := localType.TypeName() if localTypeName == "" { return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported) } essentialName := newEssentialName(localTypeName) var targetTypes []Type for _, target := range targets { namedTypes, err := target.TypesByName(essentialName) if errors.Is(err, ErrNotFound) { continue } else if err != nil { return nil, err } targetTypes = append(targetTypes, namedTypes...) } fixups, err := coreCalculateFixups(group.relos, targetTypes, bo, resolveTargetTypeID) if err != nil { return nil, fmt.Errorf("relocate %s: %w", localType, err) } for j, index := range group.indices { result[index] = fixups[j] } } return result, nil } var errAmbiguousRelocation = errors.New("ambiguous relocation") var errImpossibleRelocation = errors.New("impossible relocation") var errIncompatibleTypes = errors.New("incompatible types") // coreCalculateFixups finds the target type that best matches all relocations. // // All relos must target the same type. // // The best target is determined by scoring: the less poisoning we have to do // the better the target is. func coreCalculateFixups(relos []*CORERelocation, targets []Type, bo binary.ByteOrder, resolveTargetTypeID func(Type) (TypeID, error)) ([]COREFixup, error) { bestScore := len(relos) var bestFixups []COREFixup for _, target := range targets { score := 0 // lower is better fixups := make([]COREFixup, 0, len(relos)) for _, relo := range relos { fixup, err := coreCalculateFixup(relo, target, bo, resolveTargetTypeID) if err != nil { return nil, fmt.Errorf("target %s: %s: %w", target, relo.kind, err) } if fixup.poison || fixup.isNonExistant() { score++ } fixups = append(fixups, fixup) } if score > bestScore { // We have a better target already, ignore this one. continue } if score < bestScore { // This is the best target yet, use it. bestScore = score bestFixups = fixups continue } // Some other target has the same score as the current one. Make sure // the fixups agree with each other. for i, fixup := range bestFixups { if !fixup.equal(fixups[i]) { return nil, fmt.Errorf("%s: multiple types match: %w", fixup.kind, errAmbiguousRelocation) } } } if bestFixups == nil { // Nothing at all matched, probably because there are no suitable // targets at all. // // Poison everything except checksForExistence. bestFixups = make([]COREFixup, len(relos)) for i, relo := range relos { if relo.kind.checksForExistence() { bestFixups[i] = COREFixup{kind: relo.kind, local: 1, target: 0} } else { bestFixups[i] = COREFixup{kind: relo.kind, poison: true} } } } return bestFixups, nil } var errNoSignedness = errors.New("no signedness") // coreCalculateFixup calculates the fixup given a relocation and a target type. func coreCalculateFixup(relo *CORERelocation, target Type, bo binary.ByteOrder, resolveTargetTypeID func(Type) (TypeID, error)) (COREFixup, error) { fixup := func(local, target uint64) (COREFixup, error) { return COREFixup{kind: relo.kind, local: local, target: target}, nil } fixupWithoutValidation := func(local, target uint64) (COREFixup, error) { return COREFixup{kind: relo.kind, local: local, target: target, skipLocalValidation: true}, nil } poison := func() (COREFixup, error) { if relo.kind.checksForExistence() { return fixup(1, 0) } return COREFixup{kind: relo.kind, poison: true}, nil } zero := COREFixup{} local := relo.typ switch relo.kind { case reloTypeMatches: if len(relo.accessor) > 1 || relo.accessor[0] != 0 { return zero, fmt.Errorf("unexpected accessor %v", relo.accessor) } err := coreTypesMatch(local, target, nil) if errors.Is(err, errIncompatibleTypes) { return poison() } if err != nil { return zero, err } return fixup(1, 1) case reloTypeIDTarget, reloTypeSize, reloTypeExists: if len(relo.accessor) > 1 || relo.accessor[0] != 0 { return zero, fmt.Errorf("unexpected accessor %v", relo.accessor) } err := CheckTypeCompatibility(local, target) if errors.Is(err, errIncompatibleTypes) { return poison() } if err != nil { return zero, err } switch relo.kind { case reloTypeExists: return fixup(1, 1) case reloTypeIDTarget: targetID, err := resolveTargetTypeID(target) if errors.Is(err, ErrNotFound) { // Probably a relocation trying to get the ID // of a type from a kmod. return poison() } if err != nil { return zero, err } return fixup(uint64(relo.id), uint64(targetID)) case reloTypeSize: localSize, err := Sizeof(local) if err != nil { return zero, err } targetSize, err := Sizeof(target) if err != nil { return zero, err } return fixup(uint64(localSize), uint64(targetSize)) } case reloEnumvalValue, reloEnumvalExists: localValue, targetValue, err := coreFindEnumValue(local, relo.accessor, target) if errors.Is(err, errImpossibleRelocation) { return poison() } if err != nil { return zero, err } switch relo.kind { case reloEnumvalExists: return fixup(1, 1) case reloEnumvalValue: return fixup(localValue.Value, targetValue.Value) } case reloFieldByteOffset, reloFieldByteSize, reloFieldExists, reloFieldLShiftU64, reloFieldRShiftU64, reloFieldSigned: if _, ok := As[*Fwd](target); ok { // We can't relocate fields using a forward declaration, so // skip it. If a non-forward declaration is present in the BTF // we'll find it in one of the other iterations. return poison() } localField, targetField, err := coreFindField(local, relo.accessor, target) if errors.Is(err, errImpossibleRelocation) { return poison() } if err != nil { return zero, err } maybeSkipValidation := func(f COREFixup, err error) (COREFixup, error) { f.skipLocalValidation = localField.bitfieldSize > 0 return f, err } switch relo.kind { case reloFieldExists: return fixup(1, 1) case reloFieldByteOffset: return maybeSkipValidation(fixup(uint64(localField.offset), uint64(targetField.offset))) case reloFieldByteSize: localSize, err := Sizeof(localField.Type) if err != nil { return zero, err } targetSize, err := Sizeof(targetField.Type) if err != nil { return zero, err } return maybeSkipValidation(fixup(uint64(localSize), uint64(targetSize))) case reloFieldLShiftU64: var target uint64 if bo == binary.LittleEndian { targetSize, err := targetField.sizeBits() if err != nil { return zero, err } target = uint64(64 - targetField.bitfieldOffset - targetSize) } else { loadWidth, err := Sizeof(targetField.Type) if err != nil { return zero, err } target = uint64(64 - Bits(loadWidth*8) + targetField.bitfieldOffset) } return fixupWithoutValidation(0, target) case reloFieldRShiftU64: targetSize, err := targetField.sizeBits() if err != nil { return zero, err } return fixupWithoutValidation(0, uint64(64-targetSize)) case reloFieldSigned: switch local := UnderlyingType(localField.Type).(type) { case *Enum: target, ok := As[*Enum](targetField.Type) if !ok { return zero, fmt.Errorf("target isn't *Enum but %T", targetField.Type) } return fixup(boolToUint64(local.Signed), boolToUint64(target.Signed)) case *Int: target, ok := As[*Int](targetField.Type) if !ok { return zero, fmt.Errorf("target isn't *Int but %T", targetField.Type) } return fixup( uint64(local.Encoding&Signed), uint64(target.Encoding&Signed), ) default: return zero, fmt.Errorf("type %T: %w", local, errNoSignedness) } } } return zero, ErrNotSupported } func boolToUint64(val bool) uint64 { if val { return 1 } return 0 } /* coreAccessor contains a path through a struct. It contains at least one index. * * The interpretation depends on the kind of the relocation. The following is * taken from struct bpf_core_relo in libbpf_internal.h: * * - for field-based relocations, string encodes an accessed field using * a sequence of field and array indices, separated by colon (:). It's * conceptually very close to LLVM's getelementptr ([0]) instruction's * arguments for identifying offset to a field. * - for type-based relocations, strings is expected to be just "0"; * - for enum value-based relocations, string contains an index of enum * value within its enum type; * * Example to provide a better feel. * * struct sample { * int a; * struct { * int b[10]; * }; * }; * * struct sample s = ...; * int x = &s->a; // encoded as "0:0" (a is field #0) * int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1, * // b is field #0 inside anon struct, accessing elem #5) * int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array) */ type coreAccessor []int func parseCOREAccessor(accessor string) (coreAccessor, error) { if accessor == "" { return nil, fmt.Errorf("empty accessor") } parts := strings.Split(accessor, ":") result := make(coreAccessor, 0, len(parts)) for _, part := range parts { // 31 bits to avoid overflowing int on 32 bit platforms. index, err := strconv.ParseUint(part, 10, 31) if err != nil { return nil, fmt.Errorf("accessor index %q: %s", part, err) } result = append(result, int(index)) } return result, nil } func (ca coreAccessor) String() string { strs := make([]string, 0, len(ca)) for _, i := range ca { strs = append(strs, strconv.Itoa(i)) } return strings.Join(strs, ":") } func (ca coreAccessor) enumValue(t Type) (*EnumValue, error) { e, ok := As[*Enum](t) if !ok { return nil, fmt.Errorf("not an enum: %s", t) } if len(ca) > 1 { return nil, fmt.Errorf("invalid accessor %s for enum", ca) } i := ca[0] if i >= len(e.Values) { return nil, fmt.Errorf("invalid index %d for %s", i, e) } return &e.Values[i], nil } // coreField represents the position of a "child" of a composite type from the // start of that type. // // /- start of composite // | offset * 8 | bitfieldOffset | bitfieldSize | ... | // \- start of field end of field -/ type coreField struct { Type Type // The position of the field from the start of the composite type in bytes. offset uint32 // The offset of the bitfield in bits from the start of the field. bitfieldOffset Bits // The size of the bitfield in bits. // // Zero if the field is not a bitfield. bitfieldSize Bits } func (cf *coreField) adjustOffsetToNthElement(n int) error { if n == 0 { return nil } size, err := Sizeof(cf.Type) if err != nil { return err } cf.offset += uint32(n) * uint32(size) return nil } func (cf *coreField) adjustOffsetBits(offset Bits) error { align, err := alignof(cf.Type) if err != nil { return err } // We can compute the load offset by: // 1) converting the bit offset to bytes with a flooring division. // 2) dividing and multiplying that offset by the alignment, yielding the // load size aligned offset. offsetBytes := uint32(offset/8) / uint32(align) * uint32(align) // The number of bits remaining is the bit offset less the number of bits // we can "skip" with the aligned offset. cf.bitfieldOffset = offset - Bits(offsetBytes*8) // We know that cf.offset is aligned at to at least align since we get it // from the compiler via BTF. Adding an aligned offsetBytes preserves the // alignment. cf.offset += offsetBytes return nil } func (cf *coreField) sizeBits() (Bits, error) { if cf.bitfieldSize > 0 { return cf.bitfieldSize, nil } // Someone is trying to access a non-bitfield via a bit shift relocation. // This happens when a field changes from a bitfield to a regular field // between kernel versions. Synthesise the size to make the shifts work. size, err := Sizeof(cf.Type) if err != nil { return 0, err } return Bits(size * 8), nil } // coreFindField descends into the local type using the accessor and tries to // find an equivalent field in target at each step. // // Returns the field and the offset of the field from the start of // target in bits. func coreFindField(localT Type, localAcc coreAccessor, targetT Type) (coreField, coreField, error) { local := coreField{Type: localT} target := coreField{Type: targetT} if err := coreAreMembersCompatible(local.Type, target.Type); err != nil { return coreField{}, coreField{}, fmt.Errorf("fields: %w", err) } // The first index is used to offset a pointer of the base type like // when accessing an array. if err := local.adjustOffsetToNthElement(localAcc[0]); err != nil { return coreField{}, coreField{}, err } if err := target.adjustOffsetToNthElement(localAcc[0]); err != nil { return coreField{}, coreField{}, err } var localMaybeFlex, targetMaybeFlex bool for i, acc := range localAcc[1:] { switch localType := UnderlyingType(local.Type).(type) { case composite: // For composite types acc is used to find the field in the local type, // and then we try to find a field in target with the same name. localMembers := localType.members() if acc >= len(localMembers) { return coreField{}, coreField{}, fmt.Errorf("invalid accessor %d for %s", acc, localType) } localMember := localMembers[acc] if localMember.Name == "" { localMemberType, ok := As[composite](localMember.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("unnamed field with type %s: %s", localMember.Type, ErrNotSupported) } // This is an anonymous struct or union, ignore it. local = coreField{ Type: localMemberType, offset: local.offset + localMember.Offset.Bytes(), } localMaybeFlex = false continue } targetType, ok := As[composite](target.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("target not composite: %w", errImpossibleRelocation) } targetMember, last, err := coreFindMember(targetType, localMember.Name) if err != nil { return coreField{}, coreField{}, err } local = coreField{ Type: localMember.Type, offset: local.offset, bitfieldSize: localMember.BitfieldSize, } localMaybeFlex = acc == len(localMembers)-1 target = coreField{ Type: targetMember.Type, offset: target.offset, bitfieldSize: targetMember.BitfieldSize, } targetMaybeFlex = last if local.bitfieldSize == 0 && target.bitfieldSize == 0 { local.offset += localMember.Offset.Bytes() target.offset += targetMember.Offset.Bytes() break } // Either of the members is a bitfield. Make sure we're at the // end of the accessor. if next := i + 1; next < len(localAcc[1:]) { return coreField{}, coreField{}, fmt.Errorf("can't descend into bitfield") } if err := local.adjustOffsetBits(localMember.Offset); err != nil { return coreField{}, coreField{}, err } if err := target.adjustOffsetBits(targetMember.Offset); err != nil { return coreField{}, coreField{}, err } case *Array: // For arrays, acc is the index in the target. targetType, ok := As[*Array](target.Type) if !ok { return coreField{}, coreField{}, fmt.Errorf("target not array: %w", errImpossibleRelocation) } if localType.Nelems == 0 && !localMaybeFlex { return coreField{}, coreField{}, fmt.Errorf("local type has invalid flexible array") } if targetType.Nelems == 0 && !targetMaybeFlex { return coreField{}, coreField{}, fmt.Errorf("target type has invalid flexible array") } if localType.Nelems > 0 && acc >= int(localType.Nelems) { return coreField{}, coreField{}, fmt.Errorf("invalid access of %s at index %d", localType, acc) } if targetType.Nelems > 0 && acc >= int(targetType.Nelems) { return coreField{}, coreField{}, fmt.Errorf("out of bounds access of target: %w", errImpossibleRelocation) } local = coreField{ Type: localType.Type, offset: local.offset, } localMaybeFlex = false if err := local.adjustOffsetToNthElement(acc); err != nil { return coreField{}, coreField{}, err } target = coreField{ Type: targetType.Type, offset: target.offset, } targetMaybeFlex = false if err := target.adjustOffsetToNthElement(acc); err != nil { return coreField{}, coreField{}, err } default: return coreField{}, coreField{}, fmt.Errorf("relocate field of %T: %w", localType, ErrNotSupported) } if err := coreAreMembersCompatible(local.Type, target.Type); err != nil { return coreField{}, coreField{}, err } } return local, target, nil } // coreFindMember finds a member in a composite type while handling anonymous // structs and unions. func coreFindMember(typ composite, name string) (Member, bool, error) { if name == "" { return Member{}, false, errors.New("can't search for anonymous member") } type offsetTarget struct { composite offset Bits } targets := []offsetTarget{{typ, 0}} visited := make(map[composite]bool) for i := 0; i < len(targets); i++ { target := targets[i] // Only visit targets once to prevent infinite recursion. if visited[target] { continue } if len(visited) >= maxResolveDepth { // This check is different than libbpf, which restricts the entire // path to BPF_CORE_SPEC_MAX_LEN items. return Member{}, false, fmt.Errorf("type is nested too deep") } visited[target] = true members := target.members() for j, member := range members { if member.Name == name { // NB: This is safe because member is a copy. member.Offset += target.offset return member, j == len(members)-1, nil } // The names don't match, but this member could be an anonymous struct // or union. if member.Name != "" { continue } comp, ok := As[composite](member.Type) if !ok { return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type) } targets = append(targets, offsetTarget{comp, target.offset + member.Offset}) } } return Member{}, false, fmt.Errorf("no matching member: %w", errImpossibleRelocation) } // coreFindEnumValue follows localAcc to find the equivalent enum value in target. func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localValue, targetValue *EnumValue, _ error) { localValue, err := localAcc.enumValue(local) if err != nil { return nil, nil, err } targetEnum, ok := As[*Enum](target) if !ok { return nil, nil, errImpossibleRelocation } localName := newEssentialName(localValue.Name) for i, targetValue := range targetEnum.Values { if newEssentialName(targetValue.Name) != localName { continue } return localValue, &targetEnum.Values[i], nil } return nil, nil, errImpossibleRelocation } // CheckTypeCompatibility checks local and target types for Compatibility according to CO-RE rules. // // Only layout compatibility is checked, ignoring names of the root type. func CheckTypeCompatibility(localType Type, targetType Type) error { return coreAreTypesCompatible(localType, targetType, nil) } type pair struct { A, B Type } /* The comment below is from bpf_core_types_are_compat in libbpf.c: * * Check local and target types for compatibility. This check is used for * type-based CO-RE relocations and follow slightly different rules than * field-based relocations. This function assumes that root types were already * checked for name match. Beyond that initial root-level name check, names * are completely ignored. Compatibility rules are as follows: * - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but * kind should match for local and target types (i.e., STRUCT is not * compatible with UNION); * - for ENUMs, the size is ignored; * - for INT, size and signedness are ignored; * - for ARRAY, dimensionality is ignored, element types are checked for * compatibility recursively; * - CONST/VOLATILE/RESTRICT modifiers are ignored; * - TYPEDEFs/PTRs are compatible if types they pointing to are compatible; * - FUNC_PROTOs are compatible if they have compatible signature: same * number of input args and compatible return and argument types. * These rules are not set in stone and probably will be adjusted as we get * more experience with using BPF CO-RE relocations. * * Returns errIncompatibleTypes if types are not compatible. */ func coreAreTypesCompatible(localType Type, targetType Type, visited map[pair]struct{}) error { localType = UnderlyingType(localType) targetType = UnderlyingType(targetType) if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { return fmt.Errorf("type mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) } if _, ok := visited[pair{localType, targetType}]; ok { return nil } if visited == nil { visited = make(map[pair]struct{}) } visited[pair{localType, targetType}] = struct{}{} switch lv := localType.(type) { case *Void, *Struct, *Union, *Enum, *Fwd, *Int: return nil case *Pointer: tv := targetType.(*Pointer) return coreAreTypesCompatible(lv.Target, tv.Target, visited) case *Array: tv := targetType.(*Array) if err := coreAreTypesCompatible(lv.Index, tv.Index, visited); err != nil { return err } return coreAreTypesCompatible(lv.Type, tv.Type, visited) case *FuncProto: tv := targetType.(*FuncProto) if err := coreAreTypesCompatible(lv.Return, tv.Return, visited); err != nil { return err } if len(lv.Params) != len(tv.Params) { return fmt.Errorf("function param mismatch: %w", errIncompatibleTypes) } for i, localParam := range lv.Params { targetParam := tv.Params[i] if err := coreAreTypesCompatible(localParam.Type, targetParam.Type, visited); err != nil { return err } } return nil default: return fmt.Errorf("unsupported type %T", localType) } } /* coreAreMembersCompatible checks two types for field-based relocation compatibility. * * The comment below is from bpf_core_fields_are_compat in libbpf.c: * * Check two types for compatibility for the purpose of field access * relocation. const/volatile/restrict and typedefs are skipped to ensure we * are relocating semantically compatible entities: * - any two STRUCTs/UNIONs are compatible and can be mixed; * - any two FWDs are compatible, if their names match (modulo flavor suffix); * - any two PTRs are always compatible; * - for ENUMs, names should be the same (ignoring flavor suffix) or at * least one of enums should be anonymous; * - for ENUMs, check sizes, names are ignored; * - for INT, size and signedness are ignored; * - any two FLOATs are always compatible; * - for ARRAY, dimensionality is ignored, element types are checked for * compatibility recursively; * [ NB: coreAreMembersCompatible doesn't recurse, this check is done * by coreFindField. ] * - everything else shouldn't be ever a target of relocation. * These rules are not set in stone and probably will be adjusted as we get * more experience with using BPF CO-RE relocations. * * Returns errImpossibleRelocation if the members are not compatible. */ func coreAreMembersCompatible(localType Type, targetType Type) error { localType = UnderlyingType(localType) targetType = UnderlyingType(targetType) _, lok := localType.(composite) _, tok := targetType.(composite) if lok && tok { return nil } if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { return fmt.Errorf("type mismatch: %w", errImpossibleRelocation) } switch lv := localType.(type) { case *Array, *Pointer, *Float, *Int: return nil case *Enum: tv := targetType.(*Enum) if !coreEssentialNamesMatch(lv.Name, tv.Name) { return fmt.Errorf("names %q and %q don't match: %w", lv.Name, tv.Name, errImpossibleRelocation) } return nil case *Fwd: tv := targetType.(*Fwd) if !coreEssentialNamesMatch(lv.Name, tv.Name) { return fmt.Errorf("names %q and %q don't match: %w", lv.Name, tv.Name, errImpossibleRelocation) } return nil default: return fmt.Errorf("type %s: %w", localType, ErrNotSupported) } } // coreEssentialNamesMatch compares two names while ignoring their flavour suffix. // // This should only be used on names which are in the global scope, like struct // names, typedefs or enum values. func coreEssentialNamesMatch(a, b string) bool { if a == "" || b == "" { // allow anonymous and named type to match return true } return newEssentialName(a) == newEssentialName(b) } /* The comment below is from __bpf_core_types_match in relo_core.c: * * Check that two types "match". This function assumes that root types were * already checked for name match. * * The matching relation is defined as follows: * - modifiers and typedefs are stripped (and, hence, effectively ignored) * - generally speaking types need to be of same kind (struct vs. struct, union * vs. union, etc.) * - exceptions are struct/union behind a pointer which could also match a * forward declaration of a struct or union, respectively, and enum vs. * enum64 (see below) * Then, depending on type: * - integers: * - match if size and signedness match * - arrays & pointers: * - target types are recursively matched * - structs & unions: * - local members need to exist in target with the same name * - for each member we recursively check match unless it is already behind a * pointer, in which case we only check matching names and compatible kind * - enums: * - local variants have to have a match in target by symbolic name (but not * numeric value) * - size has to match (but enum may match enum64 and vice versa) * - function pointers: * - number and position of arguments in local type has to match target * - for each argument and the return value we recursively check match */ func coreTypesMatch(localType Type, targetType Type, visited map[pair]struct{}) error { localType = UnderlyingType(localType) targetType = UnderlyingType(targetType) if !coreEssentialNamesMatch(localType.TypeName(), targetType.TypeName()) { return fmt.Errorf("type name %q don't match %q: %w", localType.TypeName(), targetType.TypeName(), errIncompatibleTypes) } if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { return fmt.Errorf("type mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) } if _, ok := visited[pair{localType, targetType}]; ok { return nil } if visited == nil { visited = make(map[pair]struct{}) } visited[pair{localType, targetType}] = struct{}{} switch lv := (localType).(type) { case *Void: case *Fwd: if targetType.(*Fwd).Kind != lv.Kind { return fmt.Errorf("fwd kind mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) } case *Enum: return coreEnumsMatch(lv, targetType.(*Enum)) case composite: tv := targetType.(composite) if len(lv.members()) > len(tv.members()) { return errIncompatibleTypes } localMembers := lv.members() targetMembers := map[string]Member{} for _, member := range tv.members() { targetMembers[member.Name] = member } for _, localMember := range localMembers { targetMember, found := targetMembers[localMember.Name] if !found { return fmt.Errorf("no field %q in %v: %w", localMember.Name, targetType, errIncompatibleTypes) } err := coreTypesMatch(localMember.Type, targetMember.Type, visited) if err != nil { return err } } case *Int: if !coreEncodingMatches(lv, targetType.(*Int)) { return fmt.Errorf("int mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) } case *Pointer: tv := targetType.(*Pointer) // Allow a pointer to a forward declaration to match a struct // or union. if fwd, ok := As[*Fwd](lv.Target); ok && fwd.matches(tv.Target) { return nil } if fwd, ok := As[*Fwd](tv.Target); ok && fwd.matches(lv.Target) { return nil } return coreTypesMatch(lv.Target, tv.Target, visited) case *Array: tv := targetType.(*Array) if lv.Nelems != tv.Nelems { return fmt.Errorf("array mismatch between %v and %v: %w", localType, targetType, errIncompatibleTypes) } return coreTypesMatch(lv.Type, tv.Type, visited) case *FuncProto: tv := targetType.(*FuncProto) if len(lv.Params) != len(tv.Params) { return fmt.Errorf("function param mismatch: %w", errIncompatibleTypes) } for i, lparam := range lv.Params { if err := coreTypesMatch(lparam.Type, tv.Params[i].Type, visited); err != nil { return err } } return coreTypesMatch(lv.Return, tv.Return, visited) default: return fmt.Errorf("unsupported type %T", localType) } return nil } // coreEncodingMatches returns true if both ints have the same size and signedness. // All encodings other than `Signed` are considered unsigned. func coreEncodingMatches(local, target *Int) bool { return local.Size == target.Size && (local.Encoding == Signed) == (target.Encoding == Signed) } // coreEnumsMatch checks two enums match, which is considered to be the case if the following is true: // - size has to match (but enum may match enum64 and vice versa) // - local variants have to have a match in target by symbolic name (but not numeric value) func coreEnumsMatch(local *Enum, target *Enum) error { if local.Size != target.Size { return fmt.Errorf("size mismatch between %v and %v: %w", local, target, errIncompatibleTypes) } // If there are more values in the local than the target, there must be at least one value in the local // that isn't in the target, and therefor the types are incompatible. if len(local.Values) > len(target.Values) { return fmt.Errorf("local has more values than target: %w", errIncompatibleTypes) } outer: for _, lv := range local.Values { for _, rv := range target.Values { if coreEssentialNamesMatch(lv.Name, rv.Name) { continue outer } } return fmt.Errorf("no match for %v in %v: %w", lv, target, errIncompatibleTypes) } return nil } golang-github-cilium-ebpf-0.21.0+ds1/btf/core_reloc_test.go000066400000000000000000000076761520243672000234740ustar00rootroot00000000000000package btf_test import ( "bytes" "io" "os" "slices" "strings" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" ) func TestCORERelocationLoad(t *testing.T) { file := testutils.NativeFile(t, "testdata/relocs-%s.elf") fh, err := os.Open(file) if err != nil { t.Fatal(err) } defer fh.Close() spec, err := ebpf.LoadCollectionSpecFromReader(fh) if err != nil { t.Fatal(err) } for _, progSpec := range spec.Programs { t.Run(progSpec.Name, func(t *testing.T) { if _, err := fh.Seek(0, io.SeekStart); err != nil { t.Fatal(err) } prog, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{ KernelTypes: spec.Types, }) testutils.SkipIfNotSupported(t, err) if strings.HasPrefix(progSpec.Name, "err_") { if err == nil { prog.Close() t.Fatal("Expected an error") } t.Log("Got expected error:", err) return } if err != nil { t.Fatal("Load program:", err) } defer prog.Close() ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Error when running:", err) } if ret != 0 { t.Error("Assertion failed on line", ret) } }) } } func TestCORERelocationRead(t *testing.T) { file := testutils.NativeFile(t, "testdata/relocs_read-%s.elf") spec, err := ebpf.LoadCollectionSpec(file) if err != nil { t.Fatal(err) } targetFile := testutils.NativeFile(t, "testdata/relocs_read_tgt-%s.elf") targetSpec, err := btf.LoadSpec(targetFile) if err != nil { t.Fatal(err) } tests := []struct { name string opts ebpf.ProgramOptions }{ { name: "KernelTypes", opts: ebpf.ProgramOptions{ KernelTypes: targetSpec, }, }, { name: "ExtraRelocationTargets", opts: ebpf.ProgramOptions{ ExtraRelocationTargets: []*btf.Spec{targetSpec}, }, }, } for _, progSpec := range spec.Programs { for _, test := range tests { t.Run(progSpec.Name+"_"+test.name, func(t *testing.T) { prog, err := ebpf.NewProgramWithOptions(progSpec, test.opts) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Load program:", err) } defer prog.Close() ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Error when running:", err) } if ret != 0 { t.Error("Assertion failed on line", ret) } }) } } } func TestLD64IMMReloc(t *testing.T) { testutils.SkipOnOldKernel(t, "5.4", "vmlinux BTF in sysfs") file := testutils.NativeFile(t, "testdata/relocs_enum-%s.elf") fh, err := os.Open(file) if err != nil { t.Fatal(err) } defer fh.Close() spec, err := ebpf.LoadCollectionSpecFromReader(fh) if err != nil { t.Fatal(err) } coll, err := ebpf.NewCollection(spec) testutils.SkipIfNotSupportedOnOS(t, err) if err != nil { t.Fatal(err) } defer coll.Close() } func TestCOREPoisonLineInfo(t *testing.T) { spec, err := ebpf.LoadCollectionSpec(testutils.NativeFile(t, "../testdata/errors-%s.elf")) qt.Assert(t, qt.IsNil(err)) var b btf.Builder raw, err := b.Marshal(nil, nil) qt.Assert(t, qt.IsNil(err)) empty, err := btf.LoadSpecFromReader(bytes.NewReader(raw)) qt.Assert(t, qt.IsNil(err)) for _, test := range []struct { name string }{ {"poisoned_single"}, {"poisoned_double"}, } { progSpec := spec.Programs[test.name] qt.Assert(t, qt.IsNotNil(progSpec)) t.Run(test.name, func(t *testing.T) { t.Log(progSpec.Instructions) _, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{ KernelTypes: empty, }) testutils.SkipIfNotSupported(t, err) var ve *ebpf.VerifierError qt.Assert(t, qt.ErrorAs(err, &ve)) found := slices.ContainsFunc(ve.Log, func(line string) bool { return strings.HasPrefix(line, "; instruction poisoned by CO-RE") }) qt.Assert(t, qt.IsTrue(found)) t.Logf("%-5v", ve) }) } } golang-github-cilium-ebpf-0.21.0+ds1/btf/core_test.go000066400000000000000000000566501520243672000223040ustar00rootroot00000000000000package btf import ( "errors" "fmt" "os" "slices" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" ) func TestCheckTypeCompatibility(t *testing.T) { tests := []struct { a, b Type compatible bool }{ {&Void{}, &Void{}, true}, {&Struct{Name: "a"}, &Struct{Name: "b"}, true}, {&Union{Name: "a"}, &Union{Name: "b"}, true}, {&Union{Name: "a"}, &Struct{Name: "b"}, false}, {&Enum{Name: "a"}, &Enum{Name: "b"}, true}, {&Fwd{Name: "a"}, &Fwd{Name: "b"}, true}, {&Int{Name: "a", Size: 2}, &Int{Name: "b", Size: 4}, true}, {&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true}, {&Pointer{Target: &Void{}}, &Void{}, false}, {&Array{Index: &Void{}, Type: &Void{}}, &Array{Index: &Void{}, Type: &Void{}}, true}, {&Array{Index: &Void{}, Type: &Int{}}, &Array{Index: &Void{}, Type: &Void{}}, false}, {&FuncProto{Return: &Int{}}, &FuncProto{Return: &Void{}}, false}, { &FuncProto{Return: &Void{}, Params: []FuncParam{{Name: "a", Type: &Void{}}}}, &FuncProto{Return: &Void{}, Params: []FuncParam{{Name: "b", Type: &Void{}}}}, true, }, { &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}}, &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Int{}}}}, false, }, { &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}, {Type: &Void{}}}}, &FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}}, false, }, {&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Int{}}, true}, {&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Void{}}, false}, } for _, test := range tests { err := CheckTypeCompatibility(test.a, test.b) if test.compatible { if err != nil { t.Errorf("Expected types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } err = CheckTypeCompatibility(test.b, test.a) if test.compatible { if err != nil { t.Errorf("Expected reversed types to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected reversed types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } } for _, invalid := range []Type{&Var{}, &Datasec{}} { err := CheckTypeCompatibility(invalid, invalid) if errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected an error for %T, not errIncompatibleTypes", invalid) } else if err == nil { t.Errorf("Expected an error for %T", invalid) } } } func TestCOREAreMembersCompatible(t *testing.T) { tests := []struct { a, b Type compatible bool }{ {&Struct{Name: "a"}, &Struct{Name: "b"}, true}, {&Union{Name: "a"}, &Union{Name: "b"}, true}, {&Union{Name: "a"}, &Struct{Name: "b"}, true}, {&Enum{Name: "a"}, &Enum{Name: "b"}, false}, {&Enum{Name: "a"}, &Enum{Name: "a___foo"}, true}, {&Enum{Name: "a"}, &Enum{Name: ""}, true}, {&Fwd{Name: "a"}, &Fwd{Name: "b"}, false}, {&Fwd{Name: "a"}, &Fwd{Name: "a___foo"}, true}, {&Fwd{Name: "a"}, &Fwd{Name: ""}, true}, {&Int{Name: "a", Size: 2}, &Int{Name: "b", Size: 4}, true}, {&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true}, {&Pointer{Target: &Void{}}, &Void{}, false}, {&Array{Type: &Int{Size: 1}}, &Array{Type: &Int{Encoding: Signed}}, true}, {&Float{Size: 2}, &Float{Size: 4}, true}, } for _, test := range tests { err := coreAreMembersCompatible(test.a, test.b) if test.compatible { if err != nil { t.Errorf("Expected members to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } else { if !errors.Is(err, errImpossibleRelocation) { t.Errorf("Expected members to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } err = coreAreMembersCompatible(test.b, test.a) if test.compatible { if err != nil { t.Errorf("Expected reversed members to be compatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } else { if !errors.Is(err, errImpossibleRelocation) { t.Errorf("Expected reversed members to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } } for _, invalid := range []Type{&Void{}, &FuncProto{}, &Var{}, &Datasec{}} { err := coreAreMembersCompatible(invalid, invalid) if errors.Is(err, errImpossibleRelocation) { t.Errorf("Expected an error for %T, not errImpossibleRelocation", invalid) } else if err == nil { t.Errorf("Expected an error for %T", invalid) } } } func TestCOREAccessor(t *testing.T) { for _, valid := range []string{ "0", "1:0", "1:0:3:34:10:1", } { _, err := parseCOREAccessor(valid) if err != nil { t.Errorf("Parse %q: %s", valid, err) } } for _, invalid := range []string{ "", "-1", ":", "0:", ":12", "4294967296", } { _, err := parseCOREAccessor(invalid) if err == nil { t.Errorf("Accepted invalid accessor %q", invalid) } } } func TestCOREFindEnumValue(t *testing.T) { a := &Enum{Values: []EnumValue{{"foo", 23}, {"bar", 42}}} b := &Enum{Values: []EnumValue{ {"foo___flavour", 0}, {"bar", 123}, {"garbage", 3}, }} invalid := []struct { name string local Type target Type acc coreAccessor err error }{ {"o-o-b accessor", a, b, coreAccessor{len(a.Values)}, nil}, {"long accessor", a, b, coreAccessor{0, 1}, nil}, {"wrong target", a, &Void{}, coreAccessor{0, 1}, nil}, { "no matching value", b, a, coreAccessor{2}, errImpossibleRelocation, }, } for _, test := range invalid { t.Run(test.name, func(t *testing.T) { _, _, err := coreFindEnumValue(test.local, test.acc, test.target) if test.err != nil && !errors.Is(err, test.err) { t.Fatalf("Expected %s, got %s", test.err, err) } if err == nil { t.Fatal("Accepted invalid case") } }) } valid := []struct { name string local, target Type acc coreAccessor localValue, targetValue uint64 }{ {"a to b", a, b, coreAccessor{0}, 23, 0}, {"b to a", b, a, coreAccessor{1}, 123, 42}, } for _, test := range valid { t.Run(test.name, func(t *testing.T) { local, target, err := coreFindEnumValue(test.local, test.acc, test.target) qt.Assert(t, qt.IsNil(err)) qt.Check(t, qt.Equals(local.Value, test.localValue)) qt.Check(t, qt.Equals(target.Value, test.targetValue)) }) } } func TestCOREFindField(t *testing.T) { ptr := &Pointer{} u16 := &Int{Size: 2} u32 := &Int{Size: 4} aFields := []Member{ {Name: "foo", Type: ptr, Offset: 8}, {Name: "bar", Type: u16, Offset: 16}, {Name: "baz", Type: u32, Offset: 32, BitfieldSize: 3}, {Name: "quux", Type: u32, Offset: 35, BitfieldSize: 10}, {Name: "quuz", Type: u32, Offset: 45, BitfieldSize: 8}, } bFields := []Member{ {Name: "foo", Type: ptr, Offset: 16}, {Name: "bar", Type: u32, Offset: 8}, {Name: "other", Offset: 4}, // baz is separated out from the other bitfields {Name: "baz", Type: u32, Offset: 64, BitfieldSize: 3}, // quux's type changes u32->u16 {Name: "quux", Type: u16, Offset: 96, BitfieldSize: 10}, // quuz becomes a normal field {Name: "quuz", Type: u16, Offset: 112}, } aStruct := &Struct{Members: aFields, Size: 48} bStruct := &Struct{Members: bFields, Size: 80} aArray := &Array{Nelems: 4, Type: u16} bArray := &Array{Nelems: 3, Type: u32} invalid := []struct { name string local, target Type acc coreAccessor err error }{ { "unsupported type", &Void{}, &Void{}, coreAccessor{0, 0}, ErrNotSupported, }, { "different types", &Union{}, &Array{Type: u16}, coreAccessor{0}, errImpossibleRelocation, }, { "invalid composite accessor", aStruct, aStruct, coreAccessor{0, len(aStruct.Members)}, nil, }, { "invalid array accessor", aArray, aArray, coreAccessor{0, int(aArray.Nelems)}, nil, }, { "o-o-b array accessor", aArray, bArray, coreAccessor{0, int(bArray.Nelems)}, errImpossibleRelocation, }, { "no match", bStruct, aStruct, coreAccessor{0, 2}, errImpossibleRelocation, }, { "incompatible match", &Union{Members: []Member{{Name: "foo", Type: &Pointer{}}}}, &Union{Members: []Member{{Name: "foo", Type: &Int{}}}}, coreAccessor{0, 0}, errImpossibleRelocation, }, { "unsized type", bStruct, &Func{}, // non-zero accessor to force calculating the offset. coreAccessor{1}, errImpossibleRelocation, }, } for _, test := range invalid { t.Run(test.name, func(t *testing.T) { _, _, err := coreFindField(test.local, test.acc, test.target) if test.err != nil && !errors.Is(err, test.err) { t.Fatalf("Expected %s, got %s", test.err, err) } if err == nil { t.Fatal("Accepted invalid case") } t.Log(err) }) } bytes := func(typ Type) uint32 { sz, err := Sizeof(typ) if err != nil { t.Fatal(err) } return uint32(sz) } anon := func(t Type, offset Bits) []Member { return []Member{{Type: t, Offset: offset}} } anonStruct := func(m ...Member) Member { return Member{Type: &Struct{Members: m}} } anonUnion := func(m ...Member) Member { return Member{Type: &Union{Members: m}} } valid := []struct { name string local Type target Type acc coreAccessor localField, targetField coreField }{ { "array[0]", aArray, bArray, coreAccessor{0, 0}, coreField{u16, 0, 0, 0}, coreField{u32, 0, 0, 0}, }, { "array[1]", aArray, bArray, coreAccessor{0, 1}, coreField{u16, bytes(aArray.Type), 0, 0}, coreField{u32, bytes(bArray.Type), 0, 0}, }, { "array[0] with base offset", aArray, bArray, coreAccessor{1, 0}, coreField{u16, bytes(aArray), 0, 0}, coreField{u32, bytes(bArray), 0, 0}, }, { "array[2] with base offset", aArray, bArray, coreAccessor{1, 2}, coreField{u16, bytes(aArray) + 2*bytes(aArray.Type), 0, 0}, coreField{u32, bytes(bArray) + 2*bytes(bArray.Type), 0, 0}, }, { "flex array", &Struct{Members: []Member{{Name: "foo", Type: &Array{Nelems: 0, Type: u16}}}}, &Struct{Members: []Member{{Name: "foo", Type: &Array{Nelems: 0, Type: u32}}}}, coreAccessor{0, 0, 9000}, coreField{u16, bytes(u16) * 9000, 0, 0}, coreField{u32, bytes(u32) * 9000, 0, 0}, }, { "struct.0", aStruct, bStruct, coreAccessor{0, 0}, coreField{ptr, 1, 0, 0}, coreField{ptr, 2, 0, 0}, }, { "struct.0 anon", aStruct, &Struct{Members: anon(bStruct, 24)}, coreAccessor{0, 0}, coreField{ptr, 1, 0, 0}, coreField{ptr, 3 + 2, 0, 0}, }, { "struct.0 with base offset", aStruct, bStruct, coreAccessor{3, 0}, coreField{ptr, 3*bytes(aStruct) + 1, 0, 0}, coreField{ptr, 3*bytes(bStruct) + 2, 0, 0}, }, { "struct.1", aStruct, bStruct, coreAccessor{0, 1}, coreField{u16, 2, 0, 0}, coreField{u32, 1, 0, 0}, }, { "struct.1 anon", aStruct, &Struct{Members: anon(bStruct, 24)}, coreAccessor{0, 1}, coreField{u16, 2, 0, 0}, coreField{u32, 3 + 1, 0, 0}, }, { "union.1", &Union{Members: aFields, Size: 32}, &Union{Members: bFields, Size: 32}, coreAccessor{0, 1}, coreField{u16, 2, 0, 0}, coreField{u32, 1, 0, 0}, }, { "interchangeable composites", &Struct{ Members: []Member{ anonStruct(anonUnion(Member{Name: "_1", Type: u16})), }, }, &Struct{ Members: []Member{ anonUnion(anonStruct(Member{Name: "_1", Type: u16})), }, }, coreAccessor{0, 0, 0, 0}, coreField{u16, 0, 0, 0}, coreField{u16, 0, 0, 0}, }, { "struct.2 (bitfield baz)", aStruct, bStruct, coreAccessor{0, 2}, coreField{u32, 4, 0, 3}, coreField{u32, 8, 0, 3}, }, { "struct.3 (bitfield quux)", aStruct, bStruct, coreAccessor{0, 3}, coreField{u32, 4, 3, 10}, coreField{u16, 12, 0, 10}, }, { "struct.4 (bitfield quuz)", aStruct, bStruct, coreAccessor{0, 4}, coreField{u32, 4, 13, 8}, coreField{u16, 14, 0, 0}, }, } allowCoreField := cmp.AllowUnexported(coreField{}) checkCOREField := func(t *testing.T, which string, got, want coreField) { t.Helper() if diff := cmp.Diff(want, got, allowCoreField); diff != "" { t.Errorf("%s mismatch (-want +got):\n%s", which, diff) } } for _, test := range valid { t.Run(test.name, func(t *testing.T) { localField, targetField, err := coreFindField(test.local, test.acc, test.target) qt.Assert(t, qt.IsNil(err)) checkCOREField(t, "local", localField, test.localField) checkCOREField(t, "target", targetField, test.targetField) }) } } func TestCOREFindFieldCyclical(t *testing.T) { members := []Member{{Name: "foo", Type: &Pointer{}}} cyclicStruct := &Struct{} cyclicStruct.Members = []Member{{Type: cyclicStruct}} cyclicUnion := &Union{} cyclicUnion.Members = []Member{{Type: cyclicUnion}} cyclicArray := &Array{Nelems: 1} cyclicArray.Type = &Pointer{Target: cyclicArray} tests := []struct { name string local, cyclic Type }{ {"struct", &Struct{Members: members}, cyclicStruct}, {"union", &Union{Members: members}, cyclicUnion}, {"array", &Array{Nelems: 2, Type: &Int{}}, cyclicArray}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, _, err := coreFindField(test.local, coreAccessor{0, 0}, test.cyclic) if !errors.Is(err, errImpossibleRelocation) { t.Fatal("Should return errImpossibleRelocation, got", err) } }) } } func TestCORERelocation(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/*.elf"), func(t *testing.T, file string) { rd, err := os.Open(file) if err != nil { t.Fatal(err) } defer rd.Close() spec, extInfos, err := LoadSpecAndExtInfosFromReader(rd) if err != nil { t.Fatal(err) } if extInfos == nil { t.Skip("No ext_infos") } errs := map[string]error{ "err_ambiguous": errAmbiguousRelocation, "err_ambiguous_flavour": errAmbiguousRelocation, } for section := range extInfos.Funcs { name := strings.TrimPrefix(section, "socket/") t.Run(name, func(t *testing.T) { var relos []*CORERelocation for _, reloInfo := range extInfos.CORERelos[section] { relos = append(relos, reloInfo.Relo) } fixups, err := CORERelocate(relos, []*Spec{spec}, spec.byteOrder, spec.TypeID) if want := errs[name]; want != nil { if !errors.Is(err, want) { t.Fatal("Expected", want, "got", err) } return } if err != nil { t.Fatal("Can't relocate against itself:", err) } for offset, fixup := range fixups { if want := fixup.local; !fixup.skipLocalValidation && want != fixup.target { // Since we're relocating against ourselves both values // should match. t.Errorf("offset %d: local %v doesn't match target %d (kind %s)", offset, fixup.local, fixup.target, fixup.kind) } } }) } }) } func TestCOREReloFieldSigned(t *testing.T) { for _, typ := range []Type{&Int{}, &Enum{}} { t.Run(fmt.Sprintf("%T with invalid target", typ), func(t *testing.T) { relo := &CORERelocation{ typ, coreAccessor{0}, reloFieldSigned, 0, } fixup, err := coreCalculateFixup(relo, &Void{}, internal.NativeEndian, dummyTypeID) qt.Assert(t, qt.IsTrue(fixup.poison)) qt.Assert(t, qt.IsNil(err)) }) } t.Run("type without signedness", func(t *testing.T) { relo := &CORERelocation{ &Array{}, coreAccessor{0}, reloFieldSigned, 0, } _, err := coreCalculateFixup(relo, &Array{}, internal.NativeEndian, dummyTypeID) qt.Assert(t, qt.ErrorIs(err, errNoSignedness)) }) } func TestCOREReloFieldShiftU64(t *testing.T) { typ := &Struct{ Members: []Member{ {Name: "A", Type: &Fwd{}}, }, } for _, relo := range []*CORERelocation{ {typ, coreAccessor{0, 0}, reloFieldRShiftU64, 1}, {typ, coreAccessor{0, 0}, reloFieldLShiftU64, 1}, } { t.Run(relo.kind.String(), func(t *testing.T) { _, err := coreCalculateFixup(relo, typ, internal.NativeEndian, dummyTypeID) qt.Assert(t, qt.ErrorIs(err, errUnsizedType)) }) } } func TestCORERelosKmodTypeID(t *testing.T) { a := &Int{Name: "a"} b := &Int{Name: "b"} relos := []*CORERelocation{ {&Int{}, coreAccessor{0}, reloTypeIDTarget, 0}, } typeID := func(t Type) (TypeID, error) { if t == a { return 42, nil } return 0, ErrNotFound } fixups, err := coreCalculateFixups( relos, []Type{a, b}, internal.NativeEndian, typeID, ) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsFalse(fixups[0].poison)) qt.Assert(t, qt.Equals(fixups[0].target, 42)) fixups, err = coreCalculateFixups( relos, []Type{b}, internal.NativeEndian, typeID, ) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(fixups[0].poison)) } func BenchmarkCORESkBuff(b *testing.B) { spec := vmlinuxTestdataSpec(b) var skb *Struct err := spec.TypeByName("sk_buff", &skb) qt.Assert(b, qt.IsNil(err)) skbID, err := spec.TypeID(skb) qt.Assert(b, qt.IsNil(err)) lenIndex := slices.IndexFunc(skb.Members, func(m Member) bool { return m.Name == "len" }) qt.Assert(b, qt.Not(qt.Equals(lenIndex, -1))) var pktHashTypes *Enum err = spec.TypeByName("pkt_hash_types", &pktHashTypes) qt.Assert(b, qt.IsNil(err)) pktHashTypesID, err := spec.TypeID(pktHashTypes) qt.Assert(b, qt.IsNil(err)) for _, relo := range []*CORERelocation{ {skb, coreAccessor{0, lenIndex}, reloFieldByteOffset, skbID}, {skb, coreAccessor{0, lenIndex}, reloFieldByteSize, skbID}, {skb, coreAccessor{0, lenIndex}, reloFieldExists, skbID}, {skb, coreAccessor{0, lenIndex}, reloFieldSigned, skbID}, {skb, coreAccessor{0, lenIndex}, reloFieldLShiftU64, skbID}, {skb, coreAccessor{0, lenIndex}, reloFieldRShiftU64, skbID}, {skb, coreAccessor{0}, reloTypeIDLocal, skbID}, {skb, coreAccessor{0}, reloTypeIDTarget, skbID}, {skb, coreAccessor{0}, reloTypeExists, skbID}, {skb, coreAccessor{0}, reloTypeSize, skbID}, {pktHashTypes, coreAccessor{0}, reloEnumvalExists, pktHashTypesID}, {pktHashTypes, coreAccessor{0}, reloEnumvalValue, pktHashTypesID}, } { b.Run(relo.kind.String(), func(b *testing.B) { b.ReportAllocs() for b.Loop() { _, err = CORERelocate([]*CORERelocation{relo}, []*Spec{spec}, spec.byteOrder, spec.TypeID) if err != nil { b.Fatal(err) } } }) } } func TestCORETypesMatch(t *testing.T) { tests := []struct { a, b Type match bool reversible bool }{ {&Void{}, &Void{}, true, true}, {&Int{Size: 32}, &Int{Size: 32}, true, true}, {&Int{Size: 64}, &Int{Size: 32}, false, true}, {&Int{Size: 32}, &Int{Size: 32, Encoding: Signed}, false, true}, {&Fwd{Name: "a"}, &Fwd{Name: "a"}, true, true}, {&Fwd{Name: "a"}, &Fwd{Name: "b___new"}, false, true}, {&Fwd{Name: "a"}, &Fwd{Name: "a___new"}, true, true}, {&Fwd{Name: "a"}, &Struct{Name: "a___new"}, false, true}, {&Fwd{Name: "a"}, &Union{Name: "a___new"}, false, true}, {&Fwd{Name: "a", Kind: FwdStruct}, &Fwd{Name: "a___new", Kind: FwdUnion}, false, true}, {&Pointer{&Fwd{Name: "a", Kind: FwdStruct}}, &Pointer{&Struct{Name: "a___new"}}, true, true}, {&Pointer{&Fwd{Name: "a", Kind: FwdUnion}}, &Pointer{&Union{Name: "a___new"}}, true, true}, {&Pointer{&Fwd{Name: "a", Kind: FwdStruct}}, &Pointer{&Union{Name: "a___new"}}, false, true}, {&Struct{Name: "a___new"}, &Union{Name: "a___new"}, false, true}, {&Pointer{&Struct{Name: "a"}}, &Pointer{&Union{Name: "a___new"}}, false, true}, { &Struct{Name: "a", Members: []Member{ {Name: "foo", Type: &Int{}}, }}, &Struct{Name: "a___new", Members: []Member{ {Name: "foo", Type: &Int{}}, }}, true, true, }, { &Struct{Name: "a", Members: []Member{ {Name: "foo", Type: &Int{}}, }}, &Struct{Name: "a___new", Members: []Member{ {Name: "foo", Type: &Int{}}, {Name: "bar", Type: &Int{}}, }}, true, false, }, { &Struct{Name: "a", Members: []Member{ {Name: "foo", Type: &Int{}}, {Name: "bar", Type: &Int{}}, }}, &Struct{Name: "a___new", Members: []Member{ {Name: "foo", Type: &Int{}}, }}, false, false, }, { &Struct{Name: "a", Members: []Member{ {Name: "bar", Type: &Int{}}, }}, &Struct{Name: "a___new", Members: []Member{ {Name: "foo", Type: &Int{}}, }}, false, false, }, { &Struct{Name: "a", Members: []Member{ {Name: "foo", Type: &Int{Encoding: Signed}}, }}, &Struct{Name: "a___new", Members: []Member{ {Name: "foo", Type: &Int{}}, }}, false, false, }, { &Enum{Name: "a", Values: []EnumValue{ {"foo", 1}, }}, &Enum{Name: "a___new", Values: []EnumValue{ {"foo", 1}, }}, true, true, }, { &Enum{Name: "a", Values: []EnumValue{ {"foo", 1}, }}, &Enum{Name: "a___new", Values: []EnumValue{ {"foo", 1}, {"bar", 2}, }}, true, false, }, { &Enum{Name: "a", Values: []EnumValue{ {"foo", 1}, {"bar", 2}, }}, &Enum{Name: "a___new", Values: []EnumValue{ {"foo", 1}, }}, false, false, }, { &Enum{Name: "a", Values: []EnumValue{ {"foo", 1}, }}, &Enum{Name: "a___new", Values: []EnumValue{ {"bar", 1}, }}, false, false, }, { &Enum{Name: "a", Values: []EnumValue{ {"foo", 1}, }, Size: 1}, &Enum{Name: "a___new", Values: []EnumValue{ {"foo", 1}, }, Size: 2}, false, false, }, { &Array{Type: &Int{}, Nelems: 2}, &Array{Type: &Int{}, Nelems: 2}, true, true, }, { &Array{Type: &Int{}, Nelems: 3}, &Array{Type: &Int{}, Nelems: 2}, false, true, }, { &Array{Type: &Void{}, Nelems: 2}, &Array{Type: &Int{}, Nelems: 2}, false, true, }, { &FuncProto{Return: &Int{}, Params: []FuncParam{ {Name: "foo", Type: &Int{}}, }}, &FuncProto{Return: &Int{}, Params: []FuncParam{ {Name: "bar", Type: &Int{}}, }}, true, true, }, { &FuncProto{Return: &Int{}, Params: []FuncParam{ {Name: "foo", Type: &Int{}}, }}, &FuncProto{Return: &Int{}, Params: []FuncParam{ {Name: "bar", Type: &Int{}}, {Name: "baz", Type: &Int{}}, }}, false, true, }, { &FuncProto{Return: &Void{}, Params: []FuncParam{ {Name: "foo", Type: &Int{}}, }}, &FuncProto{Return: &Int{}, Params: []FuncParam{ {Name: "bar", Type: &Int{}}, }}, false, true, }, { &FuncProto{Return: &Void{}, Params: []FuncParam{ {Name: "bar", Type: &Int{Encoding: Signed}}, }}, &FuncProto{Return: &Int{}, Params: []FuncParam{ {Name: "bar", Type: &Int{}}, }}, false, true, }, } for _, test := range tests { err := coreTypesMatch(test.a, test.b, nil) if test.match { if err != nil { t.Errorf("Expected types to match: %s\na = %#v\nb = %#v", err, test.a, test.b) continue } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected types to be incompatible: \na = %#v\nb = %#v", test.a, test.b) continue } } if test.reversible { err = coreTypesMatch(test.b, test.a, nil) if test.match { if err != nil { t.Errorf("Expected reversed types to match: %s\na = %#v\nb = %#v", err, test.a, test.b) } } else { if !errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected reversed types to be incompatible: %s\na = %#v\nb = %#v", err, test.a, test.b) } } } } for _, invalid := range []Type{&Var{}, &Datasec{}} { err := coreTypesMatch(invalid, invalid, nil) if errors.Is(err, errIncompatibleTypes) { t.Errorf("Expected an error for %T, not errIncompatibleTypes", invalid) } else if err == nil { t.Errorf("Expected an error for %T", invalid) } } } // dummyTypeID returns 0, nil for any passed type. func dummyTypeID(Type) (TypeID, error) { return 0, nil } golang-github-cilium-ebpf-0.21.0+ds1/btf/dedup.go000066400000000000000000000353011520243672000214040ustar00rootroot00000000000000package btf import ( "errors" "fmt" "hash/maphash" "slices" ) // deduper deduplicates BTF types by finding all types in a Type graph that are // Equivalent and replaces them with a single instance. // // See doc comments in types.go to understand the various ways in which Types // can relate to each other and how they are compared for equality. We separate // Identity (same memory location), Equivalence (same shape/layout), and // Compatibility (CO-RE compatible) to be explicit about intent. // // This deduper opportunistically uses a combination of Identity and Equivalence // to find types that can be deduplicated. type deduper struct { visited map[Type]struct{} hashCache map[hashCacheKey]uint64 // Set of types that have been deduplicated. done map[Type]Type // Map of hash to types with that hash. hashed map[uint64][]Type eqCache map[typKey]bool seed maphash.Seed } func newDeduper() *deduper { return &deduper{ make(map[Type]struct{}), make(map[hashCacheKey]uint64), make(map[Type]Type), make(map[uint64][]Type), make(map[typKey]bool), maphash.MakeSeed(), } } func (d *deduper) deduplicate(t Type) (Type, error) { // If we have already attempted to deduplicate this exact type, return the // result. if done, ok := d.done[t]; ok { return done, nil } // Visit the subtree, if a type has children, attempt to replace it with a // deduplicated version of those children. for t := range postorder(t, d.visited) { for c := range children(t) { var err error *c, err = d.hashInsert(*c) if err != nil { return nil, err } } } // Finally, deduplicate the root type itself. return d.hashInsert(t) } // hashInsert attempts to deduplicate t by hashing it and comparing against // other types with the same hash. Returns the Type to be used as the common // substitute at this position in the graph. func (d *deduper) hashInsert(t Type) (Type, error) { // If we have deduplicated this type before, return the result of that // deduplication. if done, ok := d.done[t]; ok { return done, nil } // Compute the hash of this type. Types with the same hash are candidates for // deduplication. hash, err := d.hash(t, -1) if err != nil { return nil, err } // A hash collision is possible, so we need to compare against all candidates // with the same hash. for _, candidate := range d.hashed[hash] { // Pre-size the visited slice, experimentation on VMLinux shows a capacity // of 16 to give the best performance. const visitedCapacity = 16 err := d.typesEquivalent(candidate, t, make([]Type, 0, visitedCapacity)) if errors.Is(err, errNotEquivalent) { continue } if err != nil { return nil, err } // Found a Type that's both Equivalent and hashes to the same value, choose // it as the deduplicated version. d.done[t] = candidate return candidate, nil } d.hashed[hash] = append(d.hashed[hash], t) return t, nil } // The hash of a Type is the same given its pointer and depth budget. type hashCacheKey struct { t Type depthBudget int } // hash computes a hash for t. The produced hash is the same for Types which // are similar. The hash can collide such that two different Types may produce // the same hash, so equivalence must be checked explicitly. It will recurse // into children. The initial call should use a depthBudget of -1. func (d *deduper) hash(t Type, depthBudget int) (uint64, error) { if depthBudget == 0 { return 0, nil } h := &maphash.Hash{} h.SetSeed(d.seed) switch t := t.(type) { case *Void: maphash.WriteComparable(h, kindUnknown) case *Int: maphash.WriteComparable(h, kindInt) maphash.WriteComparable(h, *t) case *Pointer: maphash.WriteComparable(h, kindPointer) // If the depth budget is positive, decrement it every time we follow a // pointer. if depthBudget > 0 { depthBudget-- } // If this is the first time we are following a pointer, set the depth // budget. This limits amount of recursion we do when hashing pointers that // form cycles. This is cheaper than tracking visited types and works // because hash collisions are allowed. if depthBudget < 0 { depthBudget = 1 // Double pointers are common in C. However, with a depth budget of 1, all // double pointers would hash the same, causing a performance issue when // checking equivalence. So we give double pointers a bit more budget. if _, ok := t.Target.(*Pointer); ok { depthBudget = 2 } } sub, err := d.hash(t.Target, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Array: maphash.WriteComparable(h, kindArray) maphash.WriteComparable(h, t.Nelems) sub, err := d.hash(t.Index, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) _, err = d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Struct, *Union: // Check the cache to avoid recomputing the hash for this type and depth // budget. key := hashCacheKey{t, depthBudget} if cached, ok := d.hashCache[key]; ok { return cached, nil } var members []Member switch t := t.(type) { case *Struct: maphash.WriteComparable(h, kindStruct) maphash.WriteComparable(h, t.Name) maphash.WriteComparable(h, t.Size) members = t.Members case *Union: maphash.WriteComparable(h, kindUnion) maphash.WriteComparable(h, t.Name) maphash.WriteComparable(h, t.Size) members = t.Members } maphash.WriteComparable(h, len(members)) for _, m := range members { maphash.WriteComparable(h, m.Name) maphash.WriteComparable(h, m.Offset) sub, err := d.hash(m.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) } sum := h.Sum64() d.hashCache[key] = sum return sum, nil case *Enum: maphash.WriteComparable(h, kindEnum) maphash.WriteComparable(h, t.Name) maphash.WriteComparable(h, t.Size) maphash.WriteComparable(h, t.Signed) for _, v := range t.Values { maphash.WriteComparable(h, v) } case *Fwd: maphash.WriteComparable(h, kindForward) maphash.WriteComparable(h, *t) case *Typedef: maphash.WriteComparable(h, kindTypedef) maphash.WriteComparable(h, t.Name) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Volatile: maphash.WriteComparable(h, kindVolatile) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Const: maphash.WriteComparable(h, kindConst) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Restrict: maphash.WriteComparable(h, kindRestrict) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Func: maphash.WriteComparable(h, kindFunc) maphash.WriteComparable(h, t.Name) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *FuncProto: // It turns out that pointers to function prototypes are common in C code, // function pointers. Function prototypes frequently have similar patterns // of [ptr, ptr] -> int, or [ptr, ptr, ptr] -> int. Causing frequent hash // collisions, for the default depth budget of 1. So allow one additional // level of pointers when we encounter a function prototype. if depthBudget >= 0 { depthBudget++ } maphash.WriteComparable(h, kindFuncProto) for _, p := range t.Params { maphash.WriteComparable(h, p.Name) sub, err := d.hash(p.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) } sub, err := d.hash(t.Return, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Var: maphash.WriteComparable(h, kindVar) maphash.WriteComparable(h, t.Name) maphash.WriteComparable(h, t.Linkage) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Datasec: maphash.WriteComparable(h, kindDatasec) maphash.WriteComparable(h, t.Name) for _, v := range t.Vars { maphash.WriteComparable(h, v.Offset) maphash.WriteComparable(h, v.Size) sub, err := d.hash(v.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) } case *declTag: maphash.WriteComparable(h, kindDeclTag) maphash.WriteComparable(h, t.Value) maphash.WriteComparable(h, t.Index) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *TypeTag: maphash.WriteComparable(h, kindTypeTag) maphash.WriteComparable(h, t.Value) sub, err := d.hash(t.Type, depthBudget) if err != nil { return 0, err } maphash.WriteComparable(h, sub) case *Float: maphash.WriteComparable(h, kindFloat) maphash.WriteComparable(h, *t) default: return 0, fmt.Errorf("unsupported type for hashing: %T", t) } return h.Sum64(), nil } type typKey struct { a Type b Type } var errNotEquivalent = errors.New("types are not equivalent") // typesEquivalent checks if two types are Equivalent. func (d *deduper) typesEquivalent(ta, tb Type, visited []Type) error { // Fast path: if Types are Identical, they are also Equivalent. if ta == tb { return nil } switch a := ta.(type) { case *Void: if _, ok := tb.(*Void); ok { return nil } return errNotEquivalent case *Int: b, ok := tb.(*Int) if !ok { return errNotEquivalent } if a.Name != b.Name || a.Size != b.Size || a.Encoding != b.Encoding { return errNotEquivalent } return nil case *Enum: b, ok := tb.(*Enum) if !ok { return errNotEquivalent } if a.Name != b.Name || len(a.Values) != len(b.Values) { return errNotEquivalent } for i := range a.Values { if a.Values[i].Name != b.Values[i].Name || a.Values[i].Value != b.Values[i].Value { return errNotEquivalent } } return nil case *Fwd: b, ok := tb.(*Fwd) if !ok { return errNotEquivalent } if a.Name != b.Name || a.Kind != b.Kind { return errNotEquivalent } return nil case *Float: b, ok := tb.(*Float) if !ok { return errNotEquivalent } if a.Name != b.Name || a.Size != b.Size { return errNotEquivalent } return nil case *Array: b, ok := tb.(*Array) if !ok { return errNotEquivalent } if a.Nelems != b.Nelems { return errNotEquivalent } if err := d.typesEquivalent(a.Index, b.Index, visited); err != nil { return err } if err := d.typesEquivalent(a.Type, b.Type, visited); err != nil { return err } return nil case *Pointer: b, ok := tb.(*Pointer) if !ok { return errNotEquivalent } // Detect cycles by tracking visited types. Assume types are Equivalent if // we have already visited this type in the current Equivalence check. if slices.Contains(visited, ta) { return nil } visited = append(visited, ta) return d.typesEquivalent(a.Target, b.Target, visited) case *Struct, *Union: // Use a cache to avoid recomputation. We only do this for composite types // since they are where types fan out the most. For other types, the // overhead of the lookup and update outweighs performance benefits. cacheKey := typKey{a: ta, b: tb} if equal, ok := d.eqCache[cacheKey]; ok { if equal { return nil } return errNotEquivalent } compErr := d.compositeEquivalent(ta, tb, visited) d.eqCache[cacheKey] = compErr == nil return compErr case *Typedef: b, ok := tb.(*Typedef) if !ok { return errNotEquivalent } if a.Name != b.Name { return errNotEquivalent } return d.typesEquivalent(a.Type, b.Type, visited) case *Volatile: b, ok := tb.(*Volatile) if !ok { return errNotEquivalent } return d.typesEquivalent(a.Type, b.Type, visited) case *Const: b, ok := tb.(*Const) if !ok { return errNotEquivalent } return d.typesEquivalent(a.Type, b.Type, visited) case *Restrict: b, ok := tb.(*Restrict) if !ok { return errNotEquivalent } return d.typesEquivalent(a.Type, b.Type, visited) case *Func: b, ok := tb.(*Func) if !ok { return errNotEquivalent } if a.Name != b.Name { return errNotEquivalent } return d.typesEquivalent(a.Type, b.Type, visited) case *FuncProto: b, ok := tb.(*FuncProto) if !ok { return errNotEquivalent } if err := d.typesEquivalent(a.Return, b.Return, visited); err != nil { return err } if len(a.Params) != len(b.Params) { return errNotEquivalent } for i := range a.Params { if a.Params[i].Name != b.Params[i].Name { return errNotEquivalent } if err := d.typesEquivalent(a.Params[i].Type, b.Params[i].Type, visited); err != nil { return err } } return nil case *Var: b, ok := tb.(*Var) if !ok { return errNotEquivalent } if a.Name != b.Name { return errNotEquivalent } if err := d.typesEquivalent(a.Type, b.Type, visited); err != nil { return err } if a.Linkage != b.Linkage { return errNotEquivalent } return nil case *Datasec: b, ok := tb.(*Datasec) if !ok { return errNotEquivalent } if a.Name != b.Name || len(a.Vars) != len(b.Vars) { return errNotEquivalent } for i := range a.Vars { if a.Vars[i].Offset != b.Vars[i].Offset || a.Vars[i].Size != b.Vars[i].Size { return errNotEquivalent } if err := d.typesEquivalent(a.Vars[i].Type, b.Vars[i].Type, visited); err != nil { return err } } return nil case *declTag: b, ok := tb.(*declTag) if !ok { return errNotEquivalent } if a.Value != b.Value || a.Index != b.Index { return errNotEquivalent } return d.typesEquivalent(a.Type, b.Type, visited) case *TypeTag: b, ok := tb.(*TypeTag) if !ok { return errNotEquivalent } if a.Value != b.Value { return errNotEquivalent } if err := d.typesEquivalent(a.Type, b.Type, visited); err != nil { return err } return nil default: return fmt.Errorf("unsupported type for equivalence: %T", a) } } // compositeEquivalent checks if two composite types (Struct or Union) are // Equivalent. func (d *deduper) compositeEquivalent(at, bt Type, visited []Type) error { var ma, mb []Member switch a := at.(type) { case *Struct: b, ok := bt.(*Struct) if !ok { return errNotEquivalent } if a.Name != b.Name || a.Size != b.Size || len(a.Members) != len(b.Members) { return errNotEquivalent } ma = a.Members mb = b.Members case *Union: b, ok := bt.(*Union) if !ok { return errNotEquivalent } if a.Name != b.Name || a.Size != b.Size || len(a.Members) != len(b.Members) { return errNotEquivalent } ma = a.Members mb = b.Members } for i := range ma { if ma[i].Name != mb[i].Name || ma[i].Offset != mb[i].Offset { return errNotEquivalent } if err := d.typesEquivalent(ma[i].Type, mb[i].Type, visited); err != nil { return err } } return nil } golang-github-cilium-ebpf-0.21.0+ds1/btf/dedup_test.go000066400000000000000000000057211520243672000224460ustar00rootroot00000000000000package btf import ( "testing" "github.com/go-quicktest/qt" ) func countTypes(typs ...Type) int { i := 0 visited := make(map[Type]struct{}) for _, typ := range typs { for range postorder(typ, visited) { i++ } } return i } func TestDedupSKBuff(t *testing.T) { vmlinux := vmlinuxTestdataBytes(t) spec, err := loadRawSpec(vmlinux, nil) qt.Assert(t, qt.IsNil(err)) var skBuffOne *Struct qt.Assert(t, qt.IsNil(spec.TypeByName("sk_buff", &skBuffOne))) skbCount := countTypes(skBuffOne) spec = spec.Copy() var skBuffTwo *Struct qt.Assert(t, qt.IsNil(spec.TypeByName("sk_buff", &skBuffTwo))) deduper := newDeduper() types := []Type{skBuffOne, skBuffTwo} for i, typ := range types { types[i], err = deduper.deduplicate(typ) } qt.Assert(t, qt.IsNil(err)) dedupedCount := countTypes(types...) qt.Assert(t, qt.Equals(skbCount, dedupedCount), qt.Commentf("Expected deduplicated sk_buff to have same number of types as original")) } func TestDedupVmlinux(t *testing.T) { vmlinux := vmlinuxTestdataBytes(t) spec1, err := loadRawSpec(vmlinux, nil) qt.Assert(t, qt.IsNil(err)) spec2 := spec1.Copy() rootTypes := func(spec *Spec) []Type { refs := make(map[Type]int) for t := range spec.All() { refs[t] = 0 } for t := range spec.All() { for child := range children(t) { refs[*child]++ } } types := make([]Type, 0) for typ := range refs { if refs[typ] == 0 { types = append(types, typ) } } return types } spec1Roots := rootTypes(spec1) spec1TypeCount := countTypes(spec1Roots...) spec2Roots := rootTypes(spec2) types := append(spec1Roots, spec2Roots...) deduper := newDeduper() for i, typ := range types { types[i], err = deduper.deduplicate(typ) qt.Assert(t, qt.IsNil(err)) } qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(countTypes(types...), spec1TypeCount), qt.Commentf("Expected deduplicated vmlinux to have same number of types as original")) } func BenchmarkDeduplicateSKBuff(b *testing.B) { b.ReportAllocs() vmlinux := vmlinuxTestdataBytes(b) base, err := loadRawSpec(vmlinux, nil) qt.Assert(b, qt.IsNil(err)) // Obtain b.N unique copies of sk_buff. types := make([]Type, 0, b.N) for range b.N { var skb *Struct if err := base.Copy().TypeByName("sk_buff", &skb); err != nil { b.Fatal(err) } types = append(types, skb) } dedup := newDeduper() b.ResetTimer() for i := range b.N { if _, err := dedup.deduplicate(types[i]); err != nil { b.Fatal(err) } } } func BenchmarkDeduplicateVMLinux(b *testing.B) { b.ReportAllocs() vmlinux := vmlinuxTestdataBytes(b) base, err := loadRawSpec(vmlinux, nil) qt.Assert(b, qt.IsNil(err)) var types [][]Type for range b.N { var specTypes []Type for typ := range base.Copy().All() { specTypes = append(specTypes, typ) } types = append(types, specTypes) } dedup := newDeduper() b.ResetTimer() for i := range b.N { for _, typ := range types[i] { if _, err := dedup.deduplicate(typ); err != nil { b.Fatal(err) } } } } golang-github-cilium-ebpf-0.21.0+ds1/btf/doc.go000066400000000000000000000003441520243672000210470ustar00rootroot00000000000000// Package btf handles data encoded according to the BPF Type Format. // // The canonical documentation lives in the Linux kernel repository and is // available at https://www.kernel.org/doc/html/latest/bpf/btf.html package btf golang-github-cilium-ebpf-0.21.0+ds1/btf/ext_info.go000066400000000000000000000544721520243672000221300ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "errors" "fmt" "io" "math" "sort" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" ) // ExtInfos contains raw, per-section extended BTF metadata from the .BTF.ext // ELF section. type ExtInfos struct { Funcs map[string]FuncOffsets Lines map[string]LineOffsets CORERelos map[string]CORERelocationOffsets } // Section returns the FuncOffsets, LineOffsets and CORERelocationOffsets for // the given section name. Returns all nils if ExtInfos is nil, or individual // nils if there is no metadata of that type for the section. func (ei *ExtInfos) Section(name string) (FuncOffsets, LineOffsets, CORERelocationOffsets) { if ei == nil { return nil, nil, nil } return ei.Funcs[name], ei.Lines[name], ei.CORERelos[name] } // loadExtInfosFromELF parses ext infos from the .BTF.ext section in an ELF. // // Returns an error wrapping ErrNotFound if no ext infos are present. func loadExtInfosFromELF(file *internal.SafeELFFile, spec *Spec) (*ExtInfos, error) { section := file.Section(".BTF.ext") if section == nil { return nil, fmt.Errorf("btf ext infos: %w", ErrNotFound) } if section.ReaderAt == nil { return nil, fmt.Errorf("compressed ext_info is not supported") } return loadExtInfos(section.ReaderAt, file.ByteOrder, spec) } // loadExtInfos parses bare ext infos. func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, error) { // Open unbuffered section reader. binary.Read() calls io.ReadFull on // the header structs, resulting in one syscall per header. headerRd := io.NewSectionReader(r, 0, math.MaxInt64) extHeader, err := parseBTFExtHeader(headerRd, bo) if err != nil { return nil, fmt.Errorf("parsing BTF extension header: %w", err) } coreHeader, err := parseBTFExtCOREHeader(headerRd, bo, extHeader) if err != nil { return nil, fmt.Errorf("parsing BTF CO-RE header: %w", err) } buf := internal.NewBufferedSectionReader(r, extHeader.funcInfoStart(), int64(extHeader.FuncInfoLen)) btfFuncInfos, err := parseFuncInfos(buf, bo, spec.strings) if err != nil { return nil, fmt.Errorf("parsing BTF function info: %w", err) } funcInfos := make(map[string]FuncOffsets, len(btfFuncInfos)) for section, bfis := range btfFuncInfos { funcInfos[section], err = newFuncOffsets(bfis, spec) if err != nil { return nil, fmt.Errorf("section %s: func infos: %w", section, err) } } buf = internal.NewBufferedSectionReader(r, extHeader.lineInfoStart(), int64(extHeader.LineInfoLen)) btfLineInfos, err := parseLineInfos(buf, bo, spec.strings) if err != nil { return nil, fmt.Errorf("parsing BTF line info: %w", err) } lineInfos := make(map[string]LineOffsets, len(btfLineInfos)) for section, blis := range btfLineInfos { lineInfos[section], err = newLineInfos(blis, spec.strings) if err != nil { return nil, fmt.Errorf("section %s: line infos: %w", section, err) } } if coreHeader == nil || coreHeader.COREReloLen == 0 { return &ExtInfos{funcInfos, lineInfos, nil}, nil } var btfCORERelos map[string][]bpfCORERelo buf = internal.NewBufferedSectionReader(r, extHeader.coreReloStart(coreHeader), int64(coreHeader.COREReloLen)) btfCORERelos, err = parseCORERelos(buf, bo, spec.strings) if err != nil { return nil, fmt.Errorf("parsing CO-RE relocation info: %w", err) } coreRelos := make(map[string]CORERelocationOffsets, len(btfCORERelos)) for section, brs := range btfCORERelos { coreRelos[section], err = newRelocationInfos(brs, spec, spec.strings) if err != nil { return nil, fmt.Errorf("section %s: CO-RE relocations: %w", section, err) } } return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil } // MarshalExtInfos encodes function and line info embedded in insns into kernel // wire format. // // If an instruction has an [asm.Comment], it will be synthesized into a mostly // empty line info. func MarshalExtInfos(insns asm.Instructions, b *Builder) (funcInfos, lineInfos []byte, _ error) { iter := insns.Iterate() for iter.Next() { if iter.Ins.Source() != nil || FuncMetadata(iter.Ins) != nil { goto marshal } } return nil, nil, nil marshal: var fiBuf, liBuf bytes.Buffer for { if fn := FuncMetadata(iter.Ins); fn != nil { fi := &FuncOffset{ Func: fn, Offset: iter.Offset, } if err := fi.marshal(&fiBuf, b); err != nil { return nil, nil, fmt.Errorf("write func info: %w", err) } } if source := iter.Ins.Source(); source != nil { var line *Line if l, ok := source.(*Line); ok { line = l } else { line = &Line{ line: source.String(), } } li := &LineOffset{ Offset: iter.Offset, Line: line, } if err := li.marshal(&liBuf, b); err != nil { return nil, nil, fmt.Errorf("write line info: %w", err) } } if !iter.Next() { break } } return fiBuf.Bytes(), liBuf.Bytes(), nil } // btfExtHeader is found at the start of the .BTF.ext section. type btfExtHeader struct { Magic uint16 Version uint8 Flags uint8 // HdrLen is larger than the size of struct btfExtHeader when it is // immediately followed by a btfExtCOREHeader. HdrLen uint32 FuncInfoOff uint32 FuncInfoLen uint32 LineInfoOff uint32 LineInfoLen uint32 } // parseBTFExtHeader parses the header of the .BTF.ext section. func parseBTFExtHeader(r io.Reader, bo binary.ByteOrder) (*btfExtHeader, error) { var header btfExtHeader if err := binary.Read(r, bo, &header); err != nil { return nil, fmt.Errorf("can't read header: %v", err) } if header.Magic != btfMagic { return nil, fmt.Errorf("incorrect magic value %v", header.Magic) } if header.Version != 1 { return nil, fmt.Errorf("unexpected version %v", header.Version) } if header.Flags != 0 { return nil, fmt.Errorf("unsupported flags %v", header.Flags) } if int64(header.HdrLen) < int64(binary.Size(&header)) { return nil, fmt.Errorf("header length shorter than btfExtHeader size") } return &header, nil } // funcInfoStart returns the offset from the beginning of the .BTF.ext section // to the start of its func_info entries. func (h *btfExtHeader) funcInfoStart() int64 { return int64(h.HdrLen + h.FuncInfoOff) } // lineInfoStart returns the offset from the beginning of the .BTF.ext section // to the start of its line_info entries. func (h *btfExtHeader) lineInfoStart() int64 { return int64(h.HdrLen + h.LineInfoOff) } // coreReloStart returns the offset from the beginning of the .BTF.ext section // to the start of its CO-RE relocation entries. func (h *btfExtHeader) coreReloStart(ch *btfExtCOREHeader) int64 { return int64(h.HdrLen + ch.COREReloOff) } // btfExtCOREHeader is found right after the btfExtHeader when its HdrLen // field is larger than its size. type btfExtCOREHeader struct { COREReloOff uint32 COREReloLen uint32 } // parseBTFExtCOREHeader parses the tail of the .BTF.ext header. If additional // header bytes are present, extHeader.HdrLen will be larger than the struct, // indicating the presence of a CO-RE extension header. func parseBTFExtCOREHeader(r io.Reader, bo binary.ByteOrder, extHeader *btfExtHeader) (*btfExtCOREHeader, error) { extHdrSize := int64(binary.Size(&extHeader)) remainder := int64(extHeader.HdrLen) - extHdrSize if remainder == 0 { return nil, nil } var coreHeader btfExtCOREHeader if err := binary.Read(r, bo, &coreHeader); err != nil { return nil, fmt.Errorf("can't read header: %v", err) } return &coreHeader, nil } type btfExtInfoSec struct { SecNameOff uint32 NumInfo uint32 } // parseExtInfoSec parses a btf_ext_info_sec header within .BTF.ext, // appearing within func_info and line_info sub-sections. // These headers appear once for each program section in the ELF and are // followed by one or more func/line_info records for the section. func parseExtInfoSec(r io.Reader, bo binary.ByteOrder, strings *stringTable) (string, *btfExtInfoSec, error) { var infoHeader btfExtInfoSec if err := binary.Read(r, bo, &infoHeader); err != nil { return "", nil, fmt.Errorf("read ext info header: %w", err) } secName, err := strings.Lookup(infoHeader.SecNameOff) if err != nil { return "", nil, fmt.Errorf("get section name: %w", err) } if secName == "" { return "", nil, fmt.Errorf("extinfo header refers to empty section name") } if infoHeader.NumInfo == 0 { return "", nil, fmt.Errorf("section %s has zero records", secName) } return secName, &infoHeader, nil } // parseExtInfoRecordSize parses the uint32 at the beginning of a func_infos // or line_infos segment that describes the length of all extInfoRecords in // that segment. func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) { const maxRecordSize = 256 var recordSize uint32 if err := binary.Read(r, bo, &recordSize); err != nil { return 0, fmt.Errorf("can't read record size: %v", err) } if recordSize < 4 { // Need at least InsnOff worth of bytes per record. return 0, errors.New("record size too short") } if recordSize > maxRecordSize { return 0, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize) } return recordSize, nil } // FuncOffsets is a slice of FuncOffsets sorted by offset. type FuncOffsets = []FuncOffset // The size of a FuncInfo in BTF wire format. var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{})) // FuncOffset represents a [btf.Func] and its raw instruction offset within a // BPF program. type FuncOffset struct { Offset asm.RawInstructionOffset Func *Func } type bpfFuncInfo struct { // Instruction offset of the function within an ELF section. InsnOff uint32 TypeID TypeID } func newFuncOffset(fi bpfFuncInfo, spec *Spec) (*FuncOffset, error) { typ, err := spec.TypeByID(fi.TypeID) if err != nil { return nil, err } fn, ok := typ.(*Func) if !ok { return nil, fmt.Errorf("type ID %d is a %T, but expected a Func", fi.TypeID, typ) } // C doesn't have anonymous functions, but check just in case. if fn.Name == "" { return nil, fmt.Errorf("func with type ID %d doesn't have a name", fi.TypeID) } return &FuncOffset{ asm.RawInstructionOffset(fi.InsnOff), fn, }, nil } func newFuncOffsets(bfis []bpfFuncInfo, spec *Spec) (FuncOffsets, error) { fos := make(FuncOffsets, 0, len(bfis)) for _, bfi := range bfis { fi, err := newFuncOffset(bfi, spec) if err != nil { return FuncOffsets{}, fmt.Errorf("offset %d: %w", bfi.InsnOff, err) } fos = append(fos, *fi) } sort.Slice(fos, func(i, j int) bool { return fos[i].Offset <= fos[j].Offset }) return fos, nil } // LoadFuncInfos parses BTF func info from kernel wire format into a // [FuncOffsets], a sorted slice of [btf.Func]s of (sub)programs within a BPF // program with their corresponding raw instruction offsets. func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncOffsets, error) { fis, err := parseFuncInfoRecords( reader, bo, FuncInfoSize, recordNum, false, ) if err != nil { return FuncOffsets{}, fmt.Errorf("parsing BTF func info: %w", err) } return newFuncOffsets(fis, spec) } // marshal into the BTF wire format. func (fi *FuncOffset) marshal(w *bytes.Buffer, b *Builder) error { id, err := b.Add(fi.Func) if err != nil { return err } bfi := bpfFuncInfo{ InsnOff: uint32(fi.Offset), TypeID: id, } buf := make([]byte, FuncInfoSize) internal.NativeEndian.PutUint32(buf, bfi.InsnOff) internal.NativeEndian.PutUint32(buf[4:], uint32(bfi.TypeID)) _, err = w.Write(buf) return err } // parseFuncInfos parses a func_info sub-section within .BTF.ext ito a map of // func infos indexed by section name. func parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfFuncInfo, error) { recordSize, err := parseExtInfoRecordSize(r, bo) if err != nil { return nil, err } result := make(map[string][]bpfFuncInfo) for { secName, infoHeader, err := parseExtInfoSec(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } if err != nil { return nil, err } records, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo, true) if err != nil { return nil, fmt.Errorf("section %v: %w", secName, err) } result[secName] = records } } // parseFuncInfoRecords parses a stream of func_infos into a funcInfos. // These records appear after a btf_ext_info_sec header in the func_info // sub-section of .BTF.ext. func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, offsetInBytes bool) ([]bpfFuncInfo, error) { var out []bpfFuncInfo var fi bpfFuncInfo if exp, got := FuncInfoSize, recordSize; exp != got { // BTF blob's record size is longer than we know how to parse. return nil, fmt.Errorf("expected FuncInfo record size %d, but BTF blob contains %d", exp, got) } for i := uint32(0); i < recordNum; i++ { if err := binary.Read(r, bo, &fi); err != nil { return nil, fmt.Errorf("can't read function info: %v", err) } if offsetInBytes { if fi.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("offset %v is not aligned with instruction size", fi.InsnOff) } // ELF tracks offset in bytes, the kernel expects raw BPF instructions. // Convert as early as possible. fi.InsnOff /= asm.InstructionSize } out = append(out, fi) } return out, nil } var LineInfoSize = uint32(binary.Size(bpfLineInfo{})) // Line represents the location and contents of a single line of source // code a BPF ELF was compiled from. type Line struct { fileName string line string lineNumber uint32 lineColumn uint32 } func (li *Line) FileName() string { return li.fileName } func (li *Line) Line() string { return li.line } func (li *Line) LineNumber() uint32 { return li.lineNumber } func (li *Line) LineColumn() uint32 { return li.lineColumn } func (li *Line) String() string { return li.line } // LineOffsets is a slice of LineOffsets sorted by offset. type LineOffsets = []LineOffset // LineOffset represents a line info and its raw instruction offset. type LineOffset struct { Offset asm.RawInstructionOffset Line *Line } // Constants for the format of bpfLineInfo.LineCol. const ( bpfLineShift = 10 bpfLineMax = (1 << (32 - bpfLineShift)) - 1 bpfColumnMax = (1 << bpfLineShift) - 1 ) type bpfLineInfo struct { // Instruction offset of the line within the whole instruction stream, in instructions. InsnOff uint32 FileNameOff uint32 LineOff uint32 LineCol uint32 } // LoadLineInfos parses BTF line info in kernel wire format. func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineOffsets, error) { lis, err := parseLineInfoRecords( reader, bo, LineInfoSize, recordNum, false, ) if err != nil { return LineOffsets{}, fmt.Errorf("parsing BTF line info: %w", err) } return newLineInfos(lis, spec.strings) } func newLineInfo(li bpfLineInfo, strings *stringTable) (LineOffset, error) { line, err := strings.LookupCached(li.LineOff) if err != nil { return LineOffset{}, fmt.Errorf("lookup of line: %w", err) } fileName, err := strings.LookupCached(li.FileNameOff) if err != nil { return LineOffset{}, fmt.Errorf("lookup of filename: %w", err) } lineNumber := li.LineCol >> bpfLineShift lineColumn := li.LineCol & bpfColumnMax return LineOffset{ asm.RawInstructionOffset(li.InsnOff), &Line{ fileName, line, lineNumber, lineColumn, }, }, nil } func newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineOffsets, error) { lis := make([]LineOffset, 0, len(blis)) for _, bli := range blis { li, err := newLineInfo(bli, strings) if err != nil { return LineOffsets{}, fmt.Errorf("offset %d: %w", bli.InsnOff, err) } lis = append(lis, li) } sort.Slice(lis, func(i, j int) bool { return lis[i].Offset <= lis[j].Offset }) return lis, nil } // marshal writes the binary representation of the LineInfo to w. func (li *LineOffset) marshal(w *bytes.Buffer, b *Builder) error { line := li.Line if line.lineNumber > bpfLineMax { return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax) } if line.lineColumn > bpfColumnMax { return fmt.Errorf("column %d exceeds %d", line.lineColumn, bpfColumnMax) } fileNameOff, err := b.addString(line.fileName) if err != nil { return fmt.Errorf("file name %q: %w", line.fileName, err) } lineOff, err := b.addString(line.line) if err != nil { return fmt.Errorf("line %q: %w", line.line, err) } bli := bpfLineInfo{ uint32(li.Offset), fileNameOff, lineOff, (line.lineNumber << bpfLineShift) | line.lineColumn, } buf := make([]byte, LineInfoSize) internal.NativeEndian.PutUint32(buf, bli.InsnOff) internal.NativeEndian.PutUint32(buf[4:], bli.FileNameOff) internal.NativeEndian.PutUint32(buf[8:], bli.LineOff) internal.NativeEndian.PutUint32(buf[12:], bli.LineCol) _, err = w.Write(buf) return err } // parseLineInfos parses a line_info sub-section within .BTF.ext ito a map of // line infos indexed by section name. func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfLineInfo, error) { recordSize, err := parseExtInfoRecordSize(r, bo) if err != nil { return nil, err } result := make(map[string][]bpfLineInfo) for { secName, infoHeader, err := parseExtInfoSec(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } if err != nil { return nil, err } records, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo, true) if err != nil { return nil, fmt.Errorf("section %v: %w", secName, err) } result[secName] = records } } // parseLineInfoRecords parses a stream of line_infos into a lineInfos. // These records appear after a btf_ext_info_sec header in the line_info // sub-section of .BTF.ext. func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, offsetInBytes bool) ([]bpfLineInfo, error) { if exp, got := uint32(binary.Size(bpfLineInfo{})), recordSize; exp != got { // BTF blob's record size is longer than we know how to parse. return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got) } out := make([]bpfLineInfo, recordNum) if err := binary.Read(r, bo, out); err != nil { return nil, fmt.Errorf("can't read line info: %v", err) } if offsetInBytes { for i := range out { li := &out[i] if li.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff) } // ELF tracks offset in bytes, the kernel expects raw BPF instructions. // Convert as early as possible. li.InsnOff /= asm.InstructionSize } } return out, nil } // bpfCORERelo matches the kernel's struct bpf_core_relo. type bpfCORERelo struct { InsnOff uint32 TypeID TypeID AccessStrOff uint32 Kind coreKind } type CORERelocation struct { // The local type of the relocation, stripped of typedefs and qualifiers. typ Type accessor coreAccessor kind coreKind // The ID of the local type in the source BTF. id TypeID } func (cr *CORERelocation) String() string { return fmt.Sprintf("CORERelocation(%s, %s[%s], local_id=%d)", cr.kind, cr.typ, cr.accessor, cr.id) } type coreRelocationMeta struct{} // CORERelocationMetadata returns the CORERelocation associated with ins. func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation { relo, _ := ins.Metadata.Get(coreRelocationMeta{}).(*CORERelocation) return relo } // WithCORERelocationMetadata associates a CORERelocation with ins and returns // the modified Instruction. func WithCORERelocationMetadata(ins asm.Instruction, relo *CORERelocation) asm.Instruction { ins.Metadata.Set(coreRelocationMeta{}, relo) return ins } // CORERelocationOffsets is a slice of CORERelocationOffsets sorted by offset. type CORERelocationOffsets = []CORERelocationOffset // CORERelocationOffset represents a CO-RE relocation and an offset at which it // should be applied. type CORERelocationOffset struct { Relo *CORERelocation Offset asm.RawInstructionOffset } func newRelocationInfo(relo bpfCORERelo, spec *Spec, strings *stringTable) (*CORERelocationOffset, error) { typ, err := spec.TypeByID(relo.TypeID) if err != nil { return nil, err } accessorStr, err := strings.Lookup(relo.AccessStrOff) if err != nil { return nil, err } accessor, err := parseCOREAccessor(accessorStr) if err != nil { return nil, fmt.Errorf("accessor %q: %s", accessorStr, err) } return &CORERelocationOffset{ &CORERelocation{ typ, accessor, relo.Kind, relo.TypeID, }, asm.RawInstructionOffset(relo.InsnOff), }, nil } func newRelocationInfos(brs []bpfCORERelo, spec *Spec, strings *stringTable) (CORERelocationOffsets, error) { rs := make(CORERelocationOffsets, 0, len(brs)) for _, br := range brs { relo, err := newRelocationInfo(br, spec, strings) if err != nil { return nil, fmt.Errorf("offset %d: %w", br.InsnOff, err) } rs = append(rs, *relo) } sort.Slice(rs, func(i, j int) bool { return rs[i].Offset < rs[j].Offset }) return rs, nil } var extInfoReloSize = binary.Size(bpfCORERelo{}) // parseCORERelos parses a core_relos sub-section within .BTF.ext ito a map of // CO-RE relocations indexed by section name. func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfCORERelo, error) { recordSize, err := parseExtInfoRecordSize(r, bo) if err != nil { return nil, err } if recordSize != uint32(extInfoReloSize) { return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize) } result := make(map[string][]bpfCORERelo) for { secName, infoHeader, err := parseExtInfoSec(r, bo, strings) if errors.Is(err, io.EOF) { return result, nil } if err != nil { return nil, err } records, err := parseCOREReloRecords(r, bo, infoHeader.NumInfo) if err != nil { return nil, fmt.Errorf("section %v: %w", secName, err) } result[secName] = records } } // parseCOREReloRecords parses a stream of CO-RE relocation entries into a // coreRelos. These records appear after a btf_ext_info_sec header in the // core_relos sub-section of .BTF.ext. func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordNum uint32) ([]bpfCORERelo, error) { var out []bpfCORERelo var relo bpfCORERelo for i := uint32(0); i < recordNum; i++ { if err := binary.Read(r, bo, &relo); err != nil { return nil, fmt.Errorf("can't read CO-RE relocation: %v", err) } if relo.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("offset %v is not aligned with instruction size", relo.InsnOff) } // ELF tracks offset in bytes, the kernel expects raw BPF instructions. // Convert as early as possible. relo.InsnOff /= asm.InstructionSize out = append(out, relo) } return out, nil } golang-github-cilium-ebpf-0.21.0+ds1/btf/ext_info_test.go000066400000000000000000000026211520243672000231540ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "strings" "testing" "github.com/cilium/ebpf/internal" "github.com/go-quicktest/qt" ) func TestParseExtInfoBigRecordSize(t *testing.T) { rd := strings.NewReader("\xff\xff\xff\xff\x00\x00\x00\x000709171295166016") table, err := readStringTable(bytes.NewReader([]byte{0}), nil) if err != nil { t.Fatal(err) } if _, err := parseFuncInfos(rd, internal.NativeEndian, table); err == nil { t.Error("Parsing func info with large record size doesn't return an error") } if _, err := parseLineInfos(rd, internal.NativeEndian, table); err == nil { t.Error("Parsing line info with large record size doesn't return an error") } } func BenchmarkParseLineInfoRecords(b *testing.B) { size := uint32(binary.Size(bpfLineInfo{})) count := uint32(4096) buf := make([]byte, size*count) b.ReportAllocs() for b.Loop() { parseLineInfoRecords(bytes.NewReader(buf), internal.NativeEndian, size, count, true) } } func TestParseLineInfoRecordsAllocations(t *testing.T) { size := uint32(binary.Size(bpfLineInfo{})) count := uint32(4096) buf := make([]byte, size*count) allocs := testing.AllocsPerRun(5, func() { parseLineInfoRecords(bytes.NewReader(buf), internal.NativeEndian, size, count, true) }) // 7 is the number of allocations on go 1.22 // what we want to test is that we are not allocating // once per record qt.Assert(t, qt.IsTrue(allocs <= 7)) } golang-github-cilium-ebpf-0.21.0+ds1/btf/feature.go000066400000000000000000000064201520243672000217360ustar00rootroot00000000000000package btf import ( "errors" "math" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // haveBTF attempts to load a BTF blob containing an Int. It should pass on any // kernel that supports BPF_BTF_LOAD. var haveBTF = internal.NewFeatureTest("BTF", func() error { // 0-length anonymous integer err := probeBTF(&Int{}) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { return internal.ErrNotSupported } return err }, "4.18") // haveMapBTF attempts to load a minimal BTF blob containing a Var. It is // used as a proxy for .bss, .data and .rodata map support, which generally // come with a Var and Datasec. These were introduced in Linux 5.2. var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", func() error { if err := haveBTF(); err != nil { return err } v := &Var{ Name: "a", Type: &Pointer{(*Void)(nil)}, } err := probeBTF(v) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { // Treat both EINVAL and EPERM as not supported: creating the map may still // succeed without Btf* attrs. return internal.ErrNotSupported } return err }, "5.2") // haveProgBTF attempts to load a BTF blob containing a Func and FuncProto. It // is used as a proxy for ext_info (func_info) support, which depends on // Func(Proto) by definition. var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", func() error { if err := haveBTF(); err != nil { return err } fn := &Func{ Name: "a", Type: &FuncProto{Return: (*Void)(nil)}, } err := probeBTF(fn) if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { return internal.ErrNotSupported } return err }, "5.0") var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", func() error { if err := haveProgBTF(); err != nil { return err } fn := &Func{ Name: "a", Type: &FuncProto{Return: (*Void)(nil)}, Linkage: GlobalFunc, } err := probeBTF(fn) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } return err }, "5.6") var haveDeclTags = internal.NewFeatureTest("BTF decl tags", func() error { if err := haveBTF(); err != nil { return err } t := &Typedef{ Name: "a", Type: &Int{}, Tags: []string{"a"}, } err := probeBTF(t) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } return err }, "5.16") var haveTypeTags = internal.NewFeatureTest("BTF type tags", func() error { if err := haveBTF(); err != nil { return err } t := &TypeTag{ Type: &Int{}, Value: "a", } err := probeBTF(t) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } return err }, "5.17") var haveEnum64 = internal.NewFeatureTest("ENUM64", func() error { if err := haveBTF(); err != nil { return err } enum := &Enum{ Size: 8, Values: []EnumValue{ {"TEST", math.MaxUint32 + 1}, }, } err := probeBTF(enum) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } return err }, "6.0") func probeBTF(typ Type) error { b, err := NewBuilder([]Type{typ}, nil) if err != nil { return err } buf, err := b.Marshal(nil, nil) if err != nil { return err } fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ Btf: sys.SlicePointer(buf), BtfSize: uint32(len(buf)), }) if err == nil { fd.Close() } return err } golang-github-cilium-ebpf-0.21.0+ds1/btf/feature_test.go000066400000000000000000000012351520243672000227740ustar00rootroot00000000000000package btf import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveBTF(t *testing.T) { testutils.CheckFeatureTest(t, haveBTF) } func TestHaveMapBTF(t *testing.T) { testutils.CheckFeatureTest(t, haveMapBTF) } func TestHaveProgBTF(t *testing.T) { testutils.CheckFeatureTest(t, haveProgBTF) } func TestHaveFuncLinkage(t *testing.T) { testutils.CheckFeatureTest(t, haveFuncLinkage) } func TestHaveDeclTags(t *testing.T) { testutils.CheckFeatureTest(t, haveDeclTags) } func TestHaveTypeTags(t *testing.T) { testutils.CheckFeatureTest(t, haveTypeTags) } func TestHaveEnum64(t *testing.T) { testutils.CheckFeatureTest(t, haveEnum64) } golang-github-cilium-ebpf-0.21.0+ds1/btf/format.go000066400000000000000000000171661520243672000216040ustar00rootroot00000000000000package btf import ( "errors" "fmt" "strings" ) var errNestedTooDeep = errors.New("nested too deep") // GoFormatter converts a Type to Go syntax. // // A zero GoFormatter is valid to use. type GoFormatter struct { w strings.Builder // Types present in this map are referred to using the given name if they // are encountered when outputting another type. Names map[Type]string // Identifier is called for each field of struct-like types. By default the // field name is used as is. Identifier func(string) string // EnumIdentifier is called for each element of an enum. By default the // name of the enum type is concatenated with Identifier(element). EnumIdentifier func(name, element string) string } // TypeDeclaration generates a Go type declaration for a BTF type. func (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) { gf.w.Reset() if err := gf.writeTypeDecl(name, typ); err != nil { return "", err } return gf.w.String(), nil } func (gf *GoFormatter) identifier(s string) string { if gf.Identifier != nil { return gf.Identifier(s) } return s } func (gf *GoFormatter) enumIdentifier(name, element string) string { if gf.EnumIdentifier != nil { return gf.EnumIdentifier(name, element) } return name + gf.identifier(element) } // writeTypeDecl outputs a declaration of the given type. // // It encodes https://golang.org/ref/spec#Type_declarations: // // type foo struct { _ structs.HostLayout; bar uint32; } // type bar int32 func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error { if name == "" { return fmt.Errorf("need a name for type %s", typ) } typ = skipQualifiers(typ) fmt.Fprintf(&gf.w, "type %s ", name) if err := gf.writeTypeLit(typ, 0); err != nil { return err } e, ok := typ.(*Enum) if !ok || len(e.Values) == 0 { return nil } gf.w.WriteString("; const ( ") for _, ev := range e.Values { id := gf.enumIdentifier(name, ev.Name) var value any if e.Signed { value = int64(ev.Value) } else { value = ev.Value } fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, value) } gf.w.WriteString(")") return nil } // writeType outputs the name of a named type or a literal describing the type. // // It encodes https://golang.org/ref/spec#Types. // // foo (if foo is a named type) // uint32 func (gf *GoFormatter) writeType(typ Type, depth int) error { typ = skipQualifiers(typ) name := gf.Names[typ] if name != "" { gf.w.WriteString(name) return nil } return gf.writeTypeLit(typ, depth) } // writeTypeLit outputs a literal describing the type. // // The function ignores named types. // // It encodes https://golang.org/ref/spec#TypeLit. // // struct { _ structs.HostLayout; bar uint32; } // uint32 func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error { depth++ if depth > maxResolveDepth { return errNestedTooDeep } var err error switch v := skipQualifiers(typ).(type) { case *Int: err = gf.writeIntLit(v) case *Enum: if !v.Signed { gf.w.WriteRune('u') } switch v.Size { case 1: gf.w.WriteString("int8") case 2: gf.w.WriteString("int16") case 4: gf.w.WriteString("int32") case 8: gf.w.WriteString("int64") default: err = fmt.Errorf("invalid enum size %d", v.Size) } case *Typedef: err = gf.writeType(v.Type, depth) case *Array: fmt.Fprintf(&gf.w, "[%d]", v.Nelems) err = gf.writeType(v.Type, depth) case *Struct: err = gf.writeStructLit(v.Size, v.Members, depth) case *Union: // Always choose the first member to represent the union in Go. err = gf.writeStructLit(v.Size, v.Members[:1], depth) case *Datasec: err = gf.writeDatasecLit(v, depth) case *Var: err = gf.writeTypeLit(v.Type, depth) default: return fmt.Errorf("type %T: %w", v, ErrNotSupported) } if err != nil { return fmt.Errorf("%s: %w", typ, err) } return nil } func (gf *GoFormatter) writeIntLit(i *Int) error { bits := i.Size * 8 switch i.Encoding { case Bool: if i.Size != 1 { return fmt.Errorf("bool with size %d", i.Size) } gf.w.WriteString("bool") case Char: if i.Size != 1 { return fmt.Errorf("char with size %d", i.Size) } // BTF doesn't have a way to specify the signedness of a char. Assume // we are dealing with unsigned, since this works nicely with []byte // in Go code. fallthrough case Unsigned, Signed: stem := "uint" if i.Encoding == Signed { stem = "int" } if i.Size > 8 { fmt.Fprintf(&gf.w, "[%d]byte /* %s%d */", i.Size, stem, i.Size*8) } else { fmt.Fprintf(&gf.w, "%s%d", stem, bits) } default: return fmt.Errorf("can't encode %s", i.Encoding) } return nil } func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error { gf.w.WriteString("struct { _ structs.HostLayout; ") prevOffset := uint32(0) skippedBitfield := false for i, m := range members { if m.BitfieldSize > 0 { skippedBitfield = true continue } offset := m.Offset.Bytes() if n := offset - prevOffset; skippedBitfield && n > 0 { fmt.Fprintf(&gf.w, "_ [%d]byte /* unsupported bitfield */; ", n) } else { gf.writePadding(n) } fieldSize, err := Sizeof(m.Type) if err != nil { return fmt.Errorf("field %d: %w", i, err) } prevOffset = offset + uint32(fieldSize) if prevOffset > size { return fmt.Errorf("field %d of size %d exceeds type size %d", i, fieldSize, size) } if err := gf.writeStructField(m, depth); err != nil { return fmt.Errorf("field %d: %w", i, err) } } gf.writePadding(size - prevOffset) gf.w.WriteString("}") return nil } func (gf *GoFormatter) writeStructField(m Member, depth int) error { if m.BitfieldSize > 0 { return fmt.Errorf("bitfields are not supported") } if m.Offset%8 != 0 { return fmt.Errorf("unsupported offset %d", m.Offset) } if m.Name == "" { // Special case a nested anonymous union like // struct foo { union { int bar; int baz }; } // by replacing the whole union with its first member. union, ok := m.Type.(*Union) if !ok { return fmt.Errorf("anonymous fields are not supported") } if len(union.Members) == 0 { return errors.New("empty anonymous union") } depth++ if depth > maxResolveDepth { return errNestedTooDeep } m := union.Members[0] size, err := Sizeof(m.Type) if err != nil { return err } if err := gf.writeStructField(m, depth); err != nil { return err } gf.writePadding(union.Size - uint32(size)) return nil } fmt.Fprintf(&gf.w, "%s ", gf.identifier(m.Name)) if err := gf.writeType(m.Type, depth); err != nil { return err } gf.w.WriteString("; ") return nil } func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error { gf.w.WriteString("struct { _ structs.HostLayout; ") prevOffset := uint32(0) for i, vsi := range ds.Vars { v, ok := vsi.Type.(*Var) if !ok { return fmt.Errorf("can't format %s as part of data section", vsi.Type) } if v.Linkage != GlobalVar { // Ignore static, extern, etc. for now. continue } if v.Name == "" { return fmt.Errorf("variable %d: empty name", i) } gf.writePadding(vsi.Offset - prevOffset) prevOffset = vsi.Offset + vsi.Size fmt.Fprintf(&gf.w, "%s ", gf.identifier(v.Name)) if err := gf.writeType(v.Type, depth); err != nil { return fmt.Errorf("variable %d: %w", i, err) } gf.w.WriteString("; ") } gf.writePadding(ds.Size - prevOffset) gf.w.WriteString("}") return nil } func (gf *GoFormatter) writePadding(bytes uint32) { if bytes > 0 { fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes) } } func skipQualifiers(typ Type) Type { result := typ for depth := 0; depth <= maxResolveDepth; depth++ { switch v := (result).(type) { case qualifier: result = v.qualify() default: return result } } return &cycle{typ} } golang-github-cilium-ebpf-0.21.0+ds1/btf/format_test.go000066400000000000000000000150511520243672000226320ustar00rootroot00000000000000package btf import ( "errors" "fmt" "go/format" "math" "strings" "testing" ) func TestGoTypeDeclaration(t *testing.T) { tests := []struct { typ Type output string }{ {&Int{Size: 1}, "type t uint8"}, {&Int{Size: 1, Encoding: Bool}, "type t bool"}, {&Int{Size: 1, Encoding: Char}, "type t uint8"}, {&Int{Size: 2, Encoding: Signed}, "type t int16"}, {&Int{Size: 4, Encoding: Signed}, "type t int32"}, {&Int{Size: 8}, "type t uint64"}, {&Typedef{Name: "frob", Type: &Int{Size: 8}}, "type t uint64"}, {&Int{Size: 16}, "type t [16]byte /* uint128 */"}, {&Enum{Values: []EnumValue{{"FOO", 32}}, Size: 4}, "type t uint32; const ( tFOO t = 32; )"}, { &Enum{ Values: []EnumValue{ {"MINUS_ONE", math.MaxUint64}, {"MINUS_TWO", math.MaxUint64 - 1}, }, Size: 1, Signed: true, }, "type t int8; const ( tMINUS_ONE t = -1; tMINUS_TWO t = -2; )", }, { &Struct{ Name: "enum literals", Size: 2, Members: []Member{ {Name: "enum", Type: &Enum{Values: []EnumValue{{"BAR", 1}}, Size: 2}, Offset: 0}, }, }, "type t struct { _ structs.HostLayout; enum uint16; }", }, {&Array{Nelems: 2, Type: &Int{Size: 1}}, "type t [2]uint8"}, { &Union{ Size: 8, Members: []Member{ {Name: "a", Type: &Int{Size: 4}}, {Name: "b", Type: &Int{Size: 8}}, }, }, "type t struct { _ structs.HostLayout; a uint32; _ [4]byte; }", }, { &Struct{ Name: "field padding", Size: 16, Members: []Member{ {Name: "frob", Type: &Int{Size: 4}, Offset: 0}, {Name: "foo", Type: &Int{Size: 8}, Offset: 8 * 8}, }, }, "type t struct { _ structs.HostLayout; frob uint32; _ [4]byte; foo uint64; }", }, { &Struct{ Name: "end padding", Size: 16, Members: []Member{ {Name: "foo", Type: &Int{Size: 8}, Offset: 0}, {Name: "frob", Type: &Int{Size: 4}, Offset: 8 * 8}, }, }, "type t struct { _ structs.HostLayout; foo uint64; frob uint32; _ [4]byte; }", }, { &Struct{ Name: "bitfield", Size: 8, Members: []Member{ {Name: "foo", Type: &Int{Size: 4}, Offset: 0, BitfieldSize: 1}, {Name: "frob", Type: &Int{Size: 4}, Offset: 4 * 8}, }, }, "type t struct { _ structs.HostLayout; _ [4]byte /* unsupported bitfield */; frob uint32; }", }, { &Struct{ Name: "nested", Size: 8, Members: []Member{ { Name: "foo", Type: &Struct{ Size: 4, Members: []Member{ {Name: "bar", Type: &Int{Size: 4}, Offset: 0}, }, }, }, {Name: "frob", Type: &Int{Size: 4}, Offset: 4 * 8}, }, }, "type t struct { _ structs.HostLayout; foo struct { _ structs.HostLayout; bar uint32; }; frob uint32; }", }, { &Struct{ Name: "nested anon union", Size: 8, Members: []Member{ { Name: "", Type: &Union{ Size: 4, Members: []Member{ {Name: "foo", Type: &Int{Size: 4}, Offset: 0}, {Name: "bar", Type: &Int{Size: 4}, Offset: 0}, }, }, }, }, }, "type t struct { _ structs.HostLayout; foo uint32; _ [4]byte; }", }, { &Datasec{ Size: 16, Vars: []VarSecinfo{ {&Var{Name: "s", Type: &Int{Size: 2}, Linkage: StaticVar}, 0, 2}, {&Var{Name: "g", Type: &Int{Size: 4}, Linkage: GlobalVar}, 4, 4}, {&Var{Name: "e", Type: &Int{Size: 8}, Linkage: ExternVar}, 8, 8}, }, }, "type t struct { _ structs.HostLayout; _ [4]byte; g uint32; _ [8]byte; }", }, {&Var{Type: &Int{Size: 4}}, "type t uint32"}, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { have := mustGoTypeDeclaration(t, test.typ, nil, nil) if have != test.output { t.Errorf("Unexpected output:\n\t-%s\n\t+%s", test.output, have) } }) } } func TestGoTypeDeclarationNamed(t *testing.T) { e1 := &Enum{Name: "e1", Size: 4} s1 := &Struct{ Name: "s1", Size: 4, Members: []Member{ {Name: "frob", Type: e1}, }, } s2 := &Struct{ Name: "s2", Size: 4, Members: []Member{ {Name: "frood", Type: s1}, }, } td := &Typedef{Name: "td", Type: e1} arr := &Array{Nelems: 1, Type: td} tests := []struct { typ Type named []Type output string }{ {e1, []Type{e1}, "type t uint32"}, {s1, []Type{e1, s1}, "type t struct { _ structs.HostLayout; frob E1; }"}, {s2, []Type{e1}, "type t struct { _ structs.HostLayout; frood struct { _ structs.HostLayout; frob E1; }; }"}, {s2, []Type{e1, s1}, "type t struct { _ structs.HostLayout; frood S1; }"}, {td, nil, "type t uint32"}, {td, []Type{td}, "type t uint32"}, {arr, []Type{td}, "type t [1]TD"}, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { names := make(map[Type]string) for _, t := range test.named { names[t] = strings.ToUpper(t.TypeName()) } have := mustGoTypeDeclaration(t, test.typ, names, nil) if have != test.output { t.Errorf("Unexpected output:\n\t-%s\n\t+%s", test.output, have) } }) } } func TestGoTypeDeclarationQualifiers(t *testing.T) { i := &Int{Size: 4} want := mustGoTypeDeclaration(t, i, nil, nil) tests := []struct { typ Type }{ {&Volatile{Type: i}}, {&Const{Type: i}}, {&Restrict{Type: i}}, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { have := mustGoTypeDeclaration(t, test.typ, nil, nil) if have != want { t.Errorf("Unexpected output:\n\t-%s\n\t+%s", want, have) } }) } } func TestGoTypeDeclarationCycle(t *testing.T) { s := &Struct{Name: "cycle"} s.Members = []Member{{Name: "f", Type: s}} var gf GoFormatter _, err := gf.TypeDeclaration("t", s) if !errors.Is(err, errNestedTooDeep) { t.Fatal("Expected errNestedTooDeep, got", err) } } func TestRejectBogusTypes(t *testing.T) { tests := []struct { typ Type }{ {&Struct{ Size: 1, Members: []Member{ {Name: "foo", Type: &Int{Size: 2}, Offset: 0}, }, }}, {&Int{Size: 2, Encoding: Bool}}, {&Int{Size: 1, Encoding: Char | Signed}}, {&Int{Size: 2, Encoding: Char}}, } for _, test := range tests { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { var gf GoFormatter _, err := gf.TypeDeclaration("t", test.typ) if err == nil { t.Fatal("TypeDeclaration does not reject bogus type") } }) } } func mustGoTypeDeclaration(tb testing.TB, typ Type, names map[Type]string, id func(string) string) string { tb.Helper() gf := GoFormatter{ Names: names, Identifier: id, } have, err := gf.TypeDeclaration("t", typ) if err != nil { tb.Fatal(err) } _, err = format.Source([]byte(have)) if err != nil { tb.Fatalf("Output can't be formatted: %s\n%s", err, have) } return have } golang-github-cilium-ebpf-0.21.0+ds1/btf/fuzz_test.go000066400000000000000000000027131520243672000223410ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "fmt" "io" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal" ) func FuzzSpec(f *testing.F) { f.Add(mustBTFHeader(f)) f.Fuzz(func(t *testing.T, data []byte) { if len(data) < binary.Size(btfHeader{}) { t.Skip("data is too short") } spec, err := loadRawSpec(data, nil) if err != nil { if spec != nil { t.Fatal("spec is not nil") } return } if spec == nil { t.Fatal("spec is nil") } for typ, err := range spec.All() { if err == nil { fmt.Fprintf(io.Discard, "%+10v", typ) } } }) } func FuzzExtInfo(f *testing.F) { f.Add(mustBTFHeader(f), []byte("\x00foo\x00barfoo\x00")) f.Fuzz(func(t *testing.T, data, strings []byte) { if len(data) < binary.Size(btfExtHeader{}) { t.Skip("data is too short") } table, err := readStringTable(bytes.NewReader(strings), nil) if err != nil { t.Skip("invalid string table") } emptySpec := specFromTypes(t, nil) emptySpec.strings = table info, err := loadExtInfos(bytes.NewReader(data), internal.NativeEndian, emptySpec) if err != nil { if info != nil { t.Fatal("info is not nil") } } else if info == nil { t.Fatal("info is nil") } }) } func mustBTFHeader(f *testing.F) []byte { buf, err := binary.Append(nil, internal.NativeEndian, &btfHeader{ Magic: btfMagic, Version: 1, HdrLen: uint32(binary.Size(btfHeader{})), }) qt.Assert(f, qt.IsNil(err)) return buf } golang-github-cilium-ebpf-0.21.0+ds1/btf/handle.go000066400000000000000000000202241520243672000215340ustar00rootroot00000000000000package btf import ( "errors" "fmt" "math" "os" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // Handle is a reference to BTF loaded into the kernel. type Handle struct { fd *sys.FD // Size of the raw BTF in bytes. size uint32 needsKernelBase bool } // NewHandle loads the contents of a [Builder] into the kernel. // // Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF. func NewHandle(b *Builder) (*Handle, error) { small := getByteSlice() defer putByteSlice(small) buf, err := b.Marshal(*small, KernelMarshalOptions()) if err != nil { return nil, fmt.Errorf("marshal BTF: %w", err) } return NewHandleFromRawBTF(buf) } // NewHandleFromRawBTF loads raw BTF into the kernel. // // Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF. func NewHandleFromRawBTF(btf []byte) (*Handle, error) { const minLogSize = 64 * 1024 if platform.IsWindows { return nil, fmt.Errorf("btf: handle: %w", internal.ErrNotSupportedOnOS) } if uint64(len(btf)) > math.MaxUint32 { return nil, errors.New("BTF exceeds the maximum size") } attr := &sys.BtfLoadAttr{ Btf: sys.SlicePointer(btf), BtfSize: uint32(len(btf)), } var ( logBuf []byte err error ) for { var fd *sys.FD fd, err = sys.BtfLoad(attr) if err == nil { return &Handle{fd, attr.BtfSize, false}, nil } if attr.BtfLogTrueSize != 0 && attr.BtfLogSize >= attr.BtfLogTrueSize { // The log buffer already has the correct size. break } if attr.BtfLogSize != 0 && !errors.Is(err, unix.ENOSPC) { // Up until at least kernel 6.0, the BTF verifier does not return ENOSPC // if there are other verification errors. ENOSPC is only returned when // the BTF blob is correct, a log was requested, and the provided buffer // is too small. We're therefore not sure whether we got the full // log or not. break } // Make an educated guess how large the buffer should be. Start // at a reasonable minimum and then double the size. logSize := uint32(max(len(logBuf)*2, minLogSize)) if int(logSize) < len(logBuf) { return nil, errors.New("overflow while probing log buffer size") } if attr.BtfLogTrueSize != 0 { // The kernel has given us a hint how large the log buffer has to be. logSize = attr.BtfLogTrueSize } logBuf = make([]byte, logSize) attr.BtfLogSize = logSize attr.BtfLogBuf = sys.SlicePointer(logBuf) attr.BtfLogLevel = 1 } if err := haveBTF(); err != nil { return nil, err } return nil, internal.ErrorWithLog("load btf", err, logBuf) } // NewHandleFromID returns the BTF handle for a given id. // // Prefer calling [ebpf.Program.Handle] or [ebpf.Map.Handle] if possible. // // Returns ErrNotExist, if there is no BTF with the given id. // // Requires CAP_SYS_ADMIN. func NewHandleFromID(id ID) (*Handle, error) { if platform.IsWindows { return nil, fmt.Errorf("btf: handle: %w", internal.ErrNotSupportedOnOS) } fd, err := sys.BtfGetFdById(&sys.BtfGetFdByIdAttr{ Id: uint32(id), }) if err != nil { return nil, fmt.Errorf("get FD for ID %d: %w", id, err) } info, err := newHandleInfoFromFD(fd) if err != nil { _ = fd.Close() return nil, err } return &Handle{fd, info.size, info.IsModule()}, nil } // Spec parses the kernel BTF into Go types. // // base must contain type information for vmlinux if the handle is for // a kernel module. It may be nil otherwise. func (h *Handle) Spec(base *Spec) (*Spec, error) { var btfInfo sys.BtfInfo btfBuffer := make([]byte, h.size) btfInfo.Btf = sys.SlicePointer(btfBuffer) btfInfo.BtfSize = uint32(len(btfBuffer)) if err := sys.ObjInfo(h.fd, &btfInfo); err != nil { return nil, err } if h.needsKernelBase && base == nil { return nil, fmt.Errorf("missing base types") } return loadRawSpec(btfBuffer, base) } // Close destroys the handle. // // Subsequent calls to FD will return an invalid value. func (h *Handle) Close() error { if h == nil { return nil } return h.fd.Close() } // FD returns the file descriptor for the handle. func (h *Handle) FD() int { return h.fd.Int() } // Info returns metadata about the handle. func (h *Handle) Info() (*HandleInfo, error) { return newHandleInfoFromFD(h.fd) } // HandleInfo describes a Handle. type HandleInfo struct { // ID of this handle in the kernel. The ID is only valid as long as the // associated handle is kept alive. ID ID // Name is an identifying name for the BTF, currently only used by the // kernel. Name string // IsKernel is true if the BTF originated with the kernel and not // userspace. IsKernel bool // Size of the raw BTF in bytes. size uint32 } func newHandleInfoFromFD(fd *sys.FD) (*HandleInfo, error) { // We invoke the syscall once with a empty BTF and name buffers to get size // information to allocate buffers. Then we invoke it a second time with // buffers to receive the data. var btfInfo sys.BtfInfo if err := sys.ObjInfo(fd, &btfInfo); err != nil { return nil, fmt.Errorf("get BTF info for fd %s: %w", fd, err) } if btfInfo.NameLen > 0 { // NameLen doesn't account for the terminating NUL. btfInfo.NameLen++ } // Don't pull raw BTF by default, since it may be quite large. btfSize := btfInfo.BtfSize btfInfo.BtfSize = 0 nameBuffer := make([]byte, btfInfo.NameLen) btfInfo.Name = sys.SlicePointer(nameBuffer) btfInfo.NameLen = uint32(len(nameBuffer)) if err := sys.ObjInfo(fd, &btfInfo); err != nil { return nil, err } return &HandleInfo{ ID: ID(btfInfo.Id), Name: unix.ByteSliceToString(nameBuffer), IsKernel: btfInfo.KernelBtf != 0, size: btfSize, }, nil } // IsVmlinux returns true if the BTF is for the kernel itself. func (i *HandleInfo) IsVmlinux() bool { return i.IsKernel && i.Name == "vmlinux" } // IsModule returns true if the BTF is for a kernel module. func (i *HandleInfo) IsModule() bool { return i.IsKernel && i.Name != "vmlinux" } // HandleIterator allows enumerating BTF blobs loaded into the kernel. type HandleIterator struct { // The ID of the current handle. Only valid after a call to Next. ID ID // The current Handle. Only valid until a call to Next. // See Take if you want to retain the handle. Handle *Handle err error } // Next retrieves a handle for the next BTF object. // // Returns true if another BTF object was found. Call [HandleIterator.Err] after // the function returns false. func (it *HandleIterator) Next() bool { if platform.IsWindows { it.err = fmt.Errorf("btf: %w", internal.ErrNotSupportedOnOS) return false } id := it.ID for { attr := &sys.BtfGetNextIdAttr{Id: id} err := sys.BtfGetNextId(attr) if errors.Is(err, os.ErrNotExist) { // There are no more BTF objects. break } else if err != nil { it.err = fmt.Errorf("get next BTF ID: %w", err) break } id = attr.NextId handle, err := NewHandleFromID(id) if errors.Is(err, os.ErrNotExist) { // Try again with the next ID. continue } else if err != nil { it.err = fmt.Errorf("retrieve handle for ID %d: %w", id, err) break } it.Handle.Close() it.ID, it.Handle = id, handle return true } // No more handles or we encountered an error. it.Handle.Close() it.Handle = nil return false } // Take the ownership of the current handle. // // It's the callers responsibility to close the handle. func (it *HandleIterator) Take() *Handle { handle := it.Handle it.Handle = nil return handle } // Err returns an error if iteration failed for some reason. func (it *HandleIterator) Err() error { return it.err } // FindHandle returns the first handle for which predicate returns true. // // Requires CAP_SYS_ADMIN. // // Returns an error wrapping ErrNotFound if predicate never returns true or if // there is no BTF loaded into the kernel. func FindHandle(predicate func(info *HandleInfo) bool) (*Handle, error) { it := new(HandleIterator) defer it.Handle.Close() for it.Next() { info, err := it.Handle.Info() if err != nil { return nil, fmt.Errorf("info for ID %d: %w", it.ID, err) } if predicate(info) { return it.Take(), nil } } if err := it.Err(); err != nil { return nil, fmt.Errorf("iterate handles: %w", err) } return nil, fmt.Errorf("find handle: %w", ErrNotFound) } golang-github-cilium-ebpf-0.21.0+ds1/btf/handle_test.go000066400000000000000000000041071520243672000225750ustar00rootroot00000000000000package btf_test import ( "fmt" "testing" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/testutils" ) func TestHandleIterator(t *testing.T) { // There is no guarantee that there is a BTF ID allocated, but loading a module // triggers loading vmlinux. // See https://github.com/torvalds/linux/commit/5329722057d41aebc31e391907a501feaa42f7d9 testutils.SkipOnOldKernel(t, "5.11", "vmlinux BTF ID") it := new(btf.HandleIterator) defer it.Handle.Close() if !it.Next() { testutils.SkipIfNotSupportedOnOS(t, it.Err()) t.Fatalf("No BTF loaded") } if it.Handle == nil { t.Fatal("Next doesn't assign handle") } prev := it.ID for it.Next() { // Iterate all loaded BTF. if it.Handle == nil { t.Fatal("Next doesn't assign handle") } if it.ID == prev { t.Fatal("Iterator doesn't advance ID") } prev = it.ID } if err := it.Err(); err != nil { t.Fatal("Iteration returned an error:", err) } if it.Handle != nil { t.Fatal("Next doesn't clean up handle on last iteration") } if prev != it.ID { t.Fatal("Next changes ID on last iteration") } } func TestParseModuleSplitSpec(t *testing.T) { // See TestNewHandleFromID for reasoning. testutils.SkipOnOldKernel(t, "5.11", "vmlinux BTF ID") module, err := btf.FindHandle(func(info *btf.HandleInfo) bool { if info.IsModule() { t.Log("Using module", info.Name) return true } return false }) testutils.SkipIfNotSupportedOnOS(t, err) if err != nil { t.Fatal(err) } defer module.Close() vmlinux, err := btf.FindHandle(func(info *btf.HandleInfo) bool { return info.IsVmlinux() }) if err != nil { t.Fatal(err) } defer vmlinux.Close() base, err := vmlinux.Spec(nil) if err != nil { t.Fatal(err) } _, err = module.Spec(base) if err != nil { t.Fatal("Parse module BTF:", err) } } func ExampleHandleIterator() { it := new(btf.HandleIterator) defer it.Handle.Close() for it.Next() { info, err := it.Handle.Info() if err != nil { panic(err) } fmt.Printf("Found handle with ID %d and name %s\n", it.ID, info.Name) } if err := it.Err(); err != nil { panic(err) } } golang-github-cilium-ebpf-0.21.0+ds1/btf/kernel.go000066400000000000000000000173351520243672000215720ustar00rootroot00000000000000package btf import ( "errors" "fmt" "os" "path/filepath" "runtime" "slices" "sort" "sync" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/unix" ) // globalCache amortises decoding BTF across all users of the library. var globalCache = struct { sync.RWMutex kernel *Spec modules map[string]*Spec }{ modules: make(map[string]*Spec), } // FlushKernelSpec removes any cached kernel type information. func FlushKernelSpec() { globalCache.Lock() defer globalCache.Unlock() globalCache.kernel = nil globalCache.modules = make(map[string]*Spec) } // LoadKernelSpec returns the current kernel's BTF information. // // Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system // for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled. // // Consider using [Cache] instead. func LoadKernelSpec() (*Spec, error) { spec, err := loadCachedKernelSpec() return spec.Copy(), err } // load (and cache) the kernel spec. // // Does not copy Spec. func loadCachedKernelSpec() (*Spec, error) { globalCache.RLock() spec := globalCache.kernel globalCache.RUnlock() if spec != nil { return spec, nil } globalCache.Lock() defer globalCache.Unlock() // check again, to prevent race between multiple callers if globalCache.kernel != nil { return globalCache.kernel, nil } spec, err := loadKernelSpec() if err != nil { return nil, err } globalCache.kernel = spec return spec, nil } // LoadKernelModuleSpec returns the BTF information for the named kernel module. // // Using [Cache.Module] is faster when loading BTF for more than one module. // // Defaults to /sys/kernel/btf/. // Returns an error wrapping ErrNotSupported if BTF is not enabled. // Returns an error wrapping fs.ErrNotExist if BTF for the specific module doesn't exist. func LoadKernelModuleSpec(module string) (*Spec, error) { spec, err := loadCachedKernelModuleSpec(module) return spec.Copy(), err } // load (and cache) a module spec. // // Does not copy Spec. func loadCachedKernelModuleSpec(module string) (*Spec, error) { globalCache.RLock() spec := globalCache.modules[module] globalCache.RUnlock() if spec != nil { return spec, nil } base, err := loadCachedKernelSpec() if err != nil { return nil, err } // NB: This only allows a single module to be parsed at a time. Not sure // it makes a difference. globalCache.Lock() defer globalCache.Unlock() // check again, to prevent race between multiple callers if spec := globalCache.modules[module]; spec != nil { return spec, nil } spec, err = loadKernelModuleSpec(module, base) if err != nil { return nil, err } globalCache.modules[module] = spec return spec, nil } func loadKernelSpec() (*Spec, error) { if platform.IsWindows { return nil, internal.ErrNotSupportedOnOS } fh, err := os.Open("/sys/kernel/btf/vmlinux") if err == nil { defer fh.Close() info, err := fh.Stat() if err != nil { return nil, fmt.Errorf("stat vmlinux: %w", err) } // NB: It's not safe to mmap arbitrary files because mmap(2) doesn't // guarantee that changes made after mmap are not visible in the mapping. // // This is not a problem for vmlinux, since it is always a read-only file. raw, err := unix.Mmap(int(fh.Fd()), 0, int(info.Size()), unix.PROT_READ, unix.MAP_PRIVATE) if err != nil { return LoadSplitSpecFromReader(fh, nil) } spec, err := loadRawSpec(raw, nil) if err != nil { _ = unix.Munmap(raw) return nil, fmt.Errorf("load vmlinux: %w", err) } runtime.AddCleanup(spec.decoder.sharedBuf, func(b []byte) { _ = unix.Munmap(b) }, raw) return spec, nil } file, err := findVMLinux() if err != nil { return nil, err } defer file.Close() spec, err := LoadSpecFromReader(file) return spec, err } func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) { if platform.IsWindows { return nil, internal.ErrNotSupportedOnOS } dir, file := filepath.Split(module) if dir != "" || filepath.Ext(file) != "" { return nil, fmt.Errorf("invalid module name %q", module) } fh, err := os.Open(filepath.Join("/sys/kernel/btf", module)) if err != nil { return nil, err } defer fh.Close() return LoadSplitSpecFromReader(fh, base) } // findVMLinux scans multiple well-known paths for vmlinux kernel images. func findVMLinux() (*os.File, error) { if platform.IsWindows { return nil, fmt.Errorf("find vmlinux: %w", internal.ErrNotSupportedOnOS) } release, err := linux.KernelRelease() if err != nil { return nil, err } // use same list of locations as libbpf // https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122 locations := []string{ "/boot/vmlinux-%s", "/lib/modules/%s/vmlinux-%[1]s", "/lib/modules/%s/build/vmlinux", "/usr/lib/modules/%s/kernel/vmlinux", "/usr/lib/debug/boot/vmlinux-%s", "/usr/lib/debug/boot/vmlinux-%s.debug", "/usr/lib/debug/lib/modules/%s/vmlinux", } for _, loc := range locations { file, err := os.Open(fmt.Sprintf(loc, release)) if errors.Is(err, os.ErrNotExist) { continue } return file, err } return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported) } // Cache allows to amortise the cost of decoding BTF across multiple call-sites. // // It is not safe for concurrent use. type Cache struct { kernelTypes *Spec moduleTypes map[string]*Spec loadedModules []string } // NewCache creates a new Cache. // // Opportunistically reuses a global cache if possible. func NewCache() *Cache { globalCache.RLock() defer globalCache.RUnlock() // This copy is either a no-op or very cheap, since the spec won't contain // any inflated types. kernel := globalCache.kernel.Copy() if kernel == nil { return &Cache{} } modules := make(map[string]*Spec, len(globalCache.modules)) for name, spec := range globalCache.modules { decoder, _ := rebaseDecoder(spec.decoder, kernel.decoder) // NB: Kernel module BTF can't contain ELF fixups because it is always // read from sysfs. modules[name] = &Spec{decoder: decoder} } if len(modules) == 0 { return &Cache{kernel, nil, nil} } return &Cache{kernel, modules, nil} } // Kernel is equivalent to [LoadKernelSpec], except that repeated calls do // not copy the Spec. func (c *Cache) Kernel() (*Spec, error) { if c.kernelTypes != nil { return c.kernelTypes, nil } var err error c.kernelTypes, err = LoadKernelSpec() return c.kernelTypes, err } // Module is equivalent to [LoadKernelModuleSpec], except that repeated calls do // not copy the spec. // // All modules also share the return value of [Kernel] as their base. func (c *Cache) Module(name string) (*Spec, error) { if spec := c.moduleTypes[name]; spec != nil { return spec, nil } if c.moduleTypes == nil { c.moduleTypes = make(map[string]*Spec) } base, err := c.Kernel() if err != nil { return nil, err } spec, err := loadCachedKernelModuleSpec(name) if err != nil { return nil, err } // Important: base is shared between modules. This allows inflating common // types only once. decoder, err := rebaseDecoder(spec.decoder, base.decoder) if err != nil { return nil, err } spec = &Spec{decoder: decoder} c.moduleTypes[name] = spec return spec, err } // Modules returns a sorted list of all loaded modules. func (c *Cache) Modules() ([]string, error) { if c.loadedModules != nil { return c.loadedModules, nil } btfDir, err := os.Open("/sys/kernel/btf") if err != nil { return nil, err } defer btfDir.Close() entries, err := btfDir.Readdirnames(-1) if err != nil { return nil, err } entries = slices.DeleteFunc(entries, func(s string) bool { return s == "vmlinux" }) sort.Strings(entries) c.loadedModules = entries return entries, nil } golang-github-cilium-ebpf-0.21.0+ds1/btf/kernel_test.go000066400000000000000000000045571520243672000226330ustar00rootroot00000000000000package btf import ( "os" "runtime" "testing" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" ) func TestLoadKernelSpec(t *testing.T) { if _, err := os.Stat("/sys/kernel/btf/vmlinux"); os.IsNotExist(err) { t.Skip("/sys/kernel/btf/vmlinux not present") } spec, err := LoadKernelSpec() if err != nil { t.Fatal("Can't load kernel spec:", err) } if !testutils.IsVersionLessThan(t, "linux:6.16") { maps, err := os.ReadFile("/proc/self/maps") qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.StringContains(string(maps), " /sys/kernel/btf/vmlinux\n")) } // Prevent finalizer from unmapping vmlinux. runtime.KeepAlive(spec) } func TestLoadKernelModuleSpec(t *testing.T) { if _, err := os.Stat("/sys/kernel/btf/bpf_testmod"); os.IsNotExist(err) { t.Skip("/sys/kernel/btf/bpf_testmod not present") } _, err := LoadKernelModuleSpec("bpf_testmod") qt.Assert(t, qt.IsNil(err)) } func TestCache(t *testing.T) { FlushKernelSpec() c := NewCache() qt.Assert(t, qt.IsNil(c.kernelTypes)) qt.Assert(t, qt.HasLen(c.moduleTypes, 0)) qt.Assert(t, qt.IsNil(c.loadedModules)) // Test that Kernel() creates only one copy spec1, err := c.Kernel() testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNotNil(spec1)) spec2, err := c.Kernel() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNotNil(spec2)) qt.Assert(t, qt.Equals(spec1, spec2)) // Test that Module() creates only one copy mod1, err := c.Module("bpf_testmod") if !os.IsNotExist(err) { qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNotNil(mod1)) mod2, err := c.Module("bpf_testmod") qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNotNil(mod2)) qt.Assert(t, qt.Equals(mod1, mod2)) } // Pre-populate global cache vmlinux, err := LoadKernelSpec() qt.Assert(t, qt.IsNil(err)) testmod, err := LoadKernelModuleSpec("bpf_testmod") if !os.IsNotExist(err) { qt.Assert(t, qt.IsNil(err)) } // Test that NewCache populates from global cache c = NewCache() qt.Assert(t, qt.IsNotNil(c.kernelTypes)) qt.Assert(t, qt.Not(qt.Equals(c.kernelTypes, vmlinux))) if testmod != nil { qt.Assert(t, qt.IsNotNil(c.moduleTypes["bpf_testmod"])) qt.Assert(t, qt.Not(qt.Equals(c.moduleTypes["bpf_testmod"], testmod))) } // Test that Modules only reads modules once. _, err = c.Modules() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNotNil(c.loadedModules)) } golang-github-cilium-ebpf-0.21.0+ds1/btf/marshal.go000066400000000000000000000410221520243672000217270ustar00rootroot00000000000000package btf import ( "encoding/binary" "errors" "fmt" "maps" "math" "slices" "sync" "unsafe" "github.com/cilium/ebpf/internal" ) type MarshalOptions struct { // Target byte order. Defaults to the system's native endianness. Order binary.ByteOrder // Remove function linkage information for compatibility with <5.6 kernels. StripFuncLinkage bool // Replace decl tags with a placeholder for compatibility with <5.16 kernels. ReplaceDeclTags bool // Replace TypeTags with a placeholder for compatibility with <5.17 kernels. ReplaceTypeTags bool // Replace Enum64 with a placeholder for compatibility with <6.0 kernels. ReplaceEnum64 bool // Prevent the "No type found" error when loading BTF without any types. PreventNoTypeFound bool } // KernelMarshalOptions will generate BTF suitable for the current kernel. func KernelMarshalOptions() *MarshalOptions { return &MarshalOptions{ Order: internal.NativeEndian, StripFuncLinkage: haveFuncLinkage() != nil, ReplaceDeclTags: haveDeclTags() != nil, ReplaceTypeTags: haveTypeTags() != nil, ReplaceEnum64: haveEnum64() != nil, PreventNoTypeFound: true, // All current kernels require this. } } // encoder turns Types into raw BTF. type encoder struct { MarshalOptions pending internal.Deque[Type] strings *stringTableBuilder ids map[Type]TypeID visited map[Type]struct{} lastID TypeID } var bufferPool = sync.Pool{ New: func() any { buf := make([]byte, btfHeaderLen+128) return &buf }, } func getByteSlice() *[]byte { return bufferPool.Get().(*[]byte) } func putByteSlice(buf *[]byte) { *buf = (*buf)[:0] bufferPool.Put(buf) } // Builder turns Types into raw BTF. // // The default value may be used and represents an empty BTF blob. Void is // added implicitly if necessary. type Builder struct { // Explicitly added types. types []Type // IDs for all added types which the user knows about. stableIDs map[Type]TypeID // Explicitly added strings. strings *stringTableBuilder // Deduplication data structure. deduper *deduper } type BuilderOptions struct { // Deduplicate enables type deduplication. Deduplicate bool } // NewBuilder creates a Builder from a list of types. // // It is more efficient than calling [Add] individually. // // Returns an error if adding any of the types fails. func NewBuilder(types []Type, opts *BuilderOptions) (*Builder, error) { if opts == nil { opts = &BuilderOptions{} } b := &Builder{ make([]Type, 0, len(types)), make(map[Type]TypeID, len(types)), nil, nil, } if opts.Deduplicate { b.deduper = newDeduper() } for _, typ := range types { _, err := b.Add(typ) if err != nil { return nil, fmt.Errorf("add %s: %w", typ, err) } } return b, nil } // Empty returns true if neither types nor strings have been added. func (b *Builder) Empty() bool { return len(b.types) == 0 && (b.strings == nil || b.strings.Length() == 0) } // Add a Type and allocate a stable ID for it. // // Adding the identical Type multiple times is valid and will return the same ID. // // See [Type] for details on identity. func (b *Builder) Add(typ Type) (TypeID, error) { if b.stableIDs == nil { b.stableIDs = make(map[Type]TypeID) } if b.deduper != nil { var err error typ, err = b.deduper.deduplicate(typ) if err != nil { return 0, err } } if _, ok := typ.(*Void); ok { // Equality is weird for void, since it is a zero sized type. return 0, nil } if ds, ok := typ.(*Datasec); ok { if err := datasecResolveWorkaround(b, ds); err != nil { return 0, err } } id, ok := b.stableIDs[typ] if ok { return id, nil } b.types = append(b.types, typ) id = TypeID(len(b.types)) if int(id) != len(b.types) { return 0, fmt.Errorf("no more type IDs") } b.stableIDs[typ] = id return id, nil } // Spec marshals the Builder's types and returns a new Spec to query them. // // The resulting Spec does not share any state with the Builder, subsequent // additions to the Builder will not affect the Spec. func (b *Builder) Spec() (*Spec, error) { buf, err := b.Marshal(make([]byte, 0), nil) if err != nil { return nil, err } return loadRawSpec(buf, nil) } // Marshal encodes all types in the Marshaler into BTF wire format. // // opts may be nil. func (b *Builder) Marshal(buf []byte, opts *MarshalOptions) ([]byte, error) { stb := b.strings if stb == nil { // Assume that most types are named. This makes encoding large BTF like // vmlinux a lot cheaper. stb = newStringTableBuilder(len(b.types)) } else { // Avoid modifying the Builder's string table. stb = b.strings.Copy() } if opts == nil { opts = &MarshalOptions{Order: internal.NativeEndian} } // Reserve space for the BTF header. buf = slices.Grow(buf, btfHeaderLen)[:btfHeaderLen] e := encoder{ MarshalOptions: *opts, strings: stb, lastID: TypeID(len(b.types)), visited: make(map[Type]struct{}, len(b.types)), ids: maps.Clone(b.stableIDs), } if e.ids == nil { e.ids = make(map[Type]TypeID) } types := b.types if len(types) == 0 && stb.Length() > 0 && opts.PreventNoTypeFound { // We have strings that need to be written out, // but no types (besides the implicit Void). // Kernels as recent as v6.7 refuse to load such BTF // with a "No type found" error in the log. // Fix this by adding a dummy type. types = []Type{&Int{Size: 0}} } // Ensure that types are marshaled in the exact order they were Add()ed. // Otherwise the ID returned from Add() won't match. e.pending.Grow(len(types)) for _, typ := range types { e.pending.Push(typ) } buf, err := e.deflatePending(buf) if err != nil { return nil, err } length := len(buf) typeLen := uint32(length - btfHeaderLen) stringLen := e.strings.Length() buf = e.strings.AppendEncoded(buf) // Fill out the header, and write it out. header := &btfHeader{ Magic: btfMagic, Version: 1, Flags: 0, HdrLen: uint32(btfHeaderLen), TypeOff: 0, TypeLen: typeLen, StringOff: typeLen, StringLen: uint32(stringLen), } _, err = binary.Encode(buf[:btfHeaderLen], e.Order, header) if err != nil { return nil, fmt.Errorf("write header: %v", err) } return buf, nil } // addString adds a string to the resulting BTF. // // Adding the same string multiple times will return the same result. // // Returns an identifier into the string table or an error if the string // contains invalid characters. func (b *Builder) addString(str string) (uint32, error) { if b.strings == nil { b.strings = newStringTableBuilder(0) } return b.strings.Add(str) } func (e *encoder) allocateIDs(root Type) error { for typ := range postorder(root, e.visited) { if _, ok := typ.(*Void); ok { continue } if _, ok := e.ids[typ]; ok { continue } id := e.lastID + 1 if id < e.lastID { return errors.New("type ID overflow") } e.pending.Push(typ) e.ids[typ] = id e.lastID = id } return nil } // id returns the ID for the given type or panics with an error. func (e *encoder) id(typ Type) TypeID { if _, ok := typ.(*Void); ok { return 0 } id, ok := e.ids[typ] if !ok { panic(fmt.Errorf("no ID for type %v", typ)) } return id } func (e *encoder) deflatePending(buf []byte) ([]byte, error) { // Declare root outside of the loop to avoid repeated heap allocations. var root Type for !e.pending.Empty() { root = e.pending.Shift() // Allocate IDs for all children of typ, including transitive dependencies. err := e.allocateIDs(root) if err != nil { return nil, err } buf, err = e.deflateType(buf, root) if err != nil { id := e.ids[root] return nil, fmt.Errorf("deflate %v with ID %d: %w", root, id, err) } } return buf, nil } func (e *encoder) deflateType(buf []byte, typ Type) (_ []byte, err error) { defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { panic(r) } } }() var raw btfType raw.NameOff, err = e.strings.Add(typ.TypeName()) if err != nil { return nil, err } // Reserve space for the btfType header. start := len(buf) buf = append(buf, make([]byte, unsafe.Sizeof(raw))...) switch v := typ.(type) { case *Void: return nil, errors.New("Void is implicit in BTF wire format") case *Int: buf, err = e.deflateInt(buf, &raw, v) case *Pointer: raw.SetKind(kindPointer) raw.SetType(e.id(v.Target)) case *Array: raw.SetKind(kindArray) buf, err = binary.Append(buf, e.Order, &btfArray{ e.id(v.Type), e.id(v.Index), v.Nelems, }) case *Struct: raw.SetKind(kindStruct) raw.SetSize(v.Size) buf, err = e.deflateMembers(buf, &raw, v.Members) case *Union: buf, err = e.deflateUnion(buf, &raw, v) case *Enum: if v.Size == 8 { buf, err = e.deflateEnum64(buf, &raw, v) } else { buf, err = e.deflateEnum(buf, &raw, v) } case *Fwd: raw.SetKind(kindForward) raw.SetFwdKind(v.Kind) case *Typedef: raw.SetKind(kindTypedef) raw.SetType(e.id(v.Type)) case *Volatile: raw.SetKind(kindVolatile) raw.SetType(e.id(v.Type)) case *Const: e.deflateConst(&raw, v) case *Restrict: raw.SetKind(kindRestrict) raw.SetType(e.id(v.Type)) case *Func: raw.SetKind(kindFunc) raw.SetType(e.id(v.Type)) if !e.StripFuncLinkage { raw.SetLinkage(v.Linkage) } case *FuncProto: raw.SetKind(kindFuncProto) raw.SetType(e.id(v.Return)) raw.SetVlen(len(v.Params)) buf, err = e.deflateFuncParams(buf, v.Params) case *Var: raw.SetKind(kindVar) raw.SetType(e.id(v.Type)) buf, err = binary.Append(buf, e.Order, btfVariable{uint32(v.Linkage)}) case *Datasec: raw.SetKind(kindDatasec) raw.SetSize(v.Size) raw.SetVlen(len(v.Vars)) buf, err = e.deflateVarSecinfos(buf, v.Vars) case *Float: raw.SetKind(kindFloat) raw.SetSize(v.Size) case *declTag: buf, err = e.deflateDeclTag(buf, &raw, v) case *TypeTag: err = e.deflateTypeTag(&raw, v) default: return nil, fmt.Errorf("don't know how to deflate %T", v) } if err != nil { return nil, err } header := buf[start : start+int(unsafe.Sizeof(raw))] if _, err = raw.Encode(header, e.Order); err != nil { return nil, err } return buf, nil } func (e *encoder) deflateInt(buf []byte, raw *btfType, i *Int) ([]byte, error) { raw.SetKind(kindInt) raw.SetSize(i.Size) var bi btfInt bi.SetEncoding(i.Encoding) // We need to set bits in addition to size, since btf_type_int_is_regular // otherwise flags this as a bitfield. bi.SetBits(byte(i.Size) * 8) return binary.Append(buf, e.Order, bi) } func (e *encoder) deflateDeclTag(buf []byte, raw *btfType, tag *declTag) ([]byte, error) { // Replace a decl tag with an integer for compatibility with <5.16 kernels, // following libbpf behaviour. if e.ReplaceDeclTags { typ := &Int{"decl_tag_placeholder", 1, Unsigned} buf, err := e.deflateInt(buf, raw, typ) if err != nil { return nil, err } // Add the placeholder type name to the string table. The encoder added the // original type name before this call. raw.NameOff, err = e.strings.Add(typ.TypeName()) return buf, err } var err error raw.SetKind(kindDeclTag) raw.SetType(e.id(tag.Type)) raw.NameOff, err = e.strings.Add(tag.Value) if err != nil { return nil, err } return binary.Append(buf, e.Order, btfDeclTag{uint32(tag.Index)}) } func (e *encoder) deflateConst(raw *btfType, c *Const) { raw.SetKind(kindConst) raw.SetType(e.id(c.Type)) } func (e *encoder) deflateTypeTag(raw *btfType, tag *TypeTag) (err error) { // Replace a type tag with a const qualifier for compatibility with <5.17 // kernels, following libbpf behaviour. if e.ReplaceTypeTags { e.deflateConst(raw, &Const{tag.Type}) return nil } raw.SetKind(kindTypeTag) raw.SetType(e.id(tag.Type)) raw.NameOff, err = e.strings.Add(tag.Value) return } func (e *encoder) deflateUnion(buf []byte, raw *btfType, union *Union) ([]byte, error) { raw.SetKind(kindUnion) raw.SetSize(union.Size) return e.deflateMembers(buf, raw, union.Members) } func (e *encoder) deflateMembers(buf []byte, header *btfType, members []Member) ([]byte, error) { var bm btfMember isBitfield := false buf = slices.Grow(buf, len(members)*int(unsafe.Sizeof(bm))) for _, member := range members { isBitfield = isBitfield || member.BitfieldSize > 0 offset := member.Offset if isBitfield { offset = member.BitfieldSize<<24 | (member.Offset & 0xffffff) } nameOff, err := e.strings.Add(member.Name) if err != nil { return nil, err } bm = btfMember{ nameOff, e.id(member.Type), uint32(offset), } buf, err = binary.Append(buf, e.Order, &bm) if err != nil { return nil, err } } header.SetVlen(len(members)) header.SetBitfield(isBitfield) return buf, nil } func (e *encoder) deflateEnum(buf []byte, raw *btfType, enum *Enum) ([]byte, error) { raw.SetKind(kindEnum) raw.SetSize(enum.Size) raw.SetVlen(len(enum.Values)) // Signedness appeared together with ENUM64 support. raw.SetSigned(enum.Signed && !e.ReplaceEnum64) return e.deflateEnumValues(buf, enum) } func (e *encoder) deflateEnumValues(buf []byte, enum *Enum) ([]byte, error) { var be btfEnum buf = slices.Grow(buf, len(enum.Values)*int(unsafe.Sizeof(be))) for _, value := range enum.Values { nameOff, err := e.strings.Add(value.Name) if err != nil { return nil, err } if enum.Signed { if signedValue := int64(value.Value); signedValue < math.MinInt32 || signedValue > math.MaxInt32 { return nil, fmt.Errorf("value %d of enum %q exceeds 32 bits", signedValue, value.Name) } } else { if value.Value > math.MaxUint32 { return nil, fmt.Errorf("value %d of enum %q exceeds 32 bits", value.Value, value.Name) } } be = btfEnum{ nameOff, uint32(value.Value), } buf, err = binary.Append(buf, e.Order, &be) if err != nil { return nil, err } } return buf, nil } func (e *encoder) deflateEnum64(buf []byte, raw *btfType, enum *Enum) ([]byte, error) { if e.ReplaceEnum64 { // Replace the ENUM64 with a union of fields with the correct size. // This matches libbpf behaviour on purpose. placeholder := &Int{ "enum64_placeholder", enum.Size, Unsigned, } if enum.Signed { placeholder.Encoding = Signed } if err := e.allocateIDs(placeholder); err != nil { return nil, fmt.Errorf("add enum64 placeholder: %w", err) } members := make([]Member, 0, len(enum.Values)) for _, v := range enum.Values { members = append(members, Member{ Name: v.Name, Type: placeholder, }) } return e.deflateUnion(buf, raw, &Union{enum.Name, enum.Size, members, nil}) } raw.SetKind(kindEnum64) raw.SetSize(enum.Size) raw.SetVlen(len(enum.Values)) raw.SetSigned(enum.Signed) return e.deflateEnum64Values(buf, enum.Values) } func (e *encoder) deflateEnum64Values(buf []byte, values []EnumValue) ([]byte, error) { var be btfEnum64 buf = slices.Grow(buf, len(values)*int(unsafe.Sizeof(be))) for _, value := range values { nameOff, err := e.strings.Add(value.Name) if err != nil { return nil, err } be = btfEnum64{ nameOff, uint32(value.Value), uint32(value.Value >> 32), } buf, err = binary.Append(buf, e.Order, &be) if err != nil { return nil, err } } return buf, nil } func (e *encoder) deflateFuncParams(buf []byte, params []FuncParam) ([]byte, error) { var bp btfParam buf = slices.Grow(buf, len(params)*int(unsafe.Sizeof(bp))) for _, param := range params { nameOff, err := e.strings.Add(param.Name) if err != nil { return nil, err } bp = btfParam{ nameOff, e.id(param.Type), } buf, err = binary.Append(buf, e.Order, &bp) if err != nil { return nil, err } } return buf, nil } func (e *encoder) deflateVarSecinfos(buf []byte, vars []VarSecinfo) ([]byte, error) { var vsi btfVarSecinfo var err error buf = slices.Grow(buf, len(vars)*int(unsafe.Sizeof(vsi))) for _, v := range vars { vsi = btfVarSecinfo{ e.id(v.Type), v.Offset, v.Size, } buf, err = binary.Append(buf, e.Order, vsi) if err != nil { return nil, err } } return buf, nil } // MarshalMapKV creates a BTF object containing a map key and value. // // The function is intended for the use of the ebpf package and may be removed // at any point in time. func MarshalMapKV(key, value Type) (_ *Handle, keyID, valueID TypeID, err error) { var b Builder if key != nil { keyID, err = b.Add(key) if err != nil { return nil, 0, 0, fmt.Errorf("add key type: %w", err) } } if value != nil { valueID, err = b.Add(value) if err != nil { return nil, 0, 0, fmt.Errorf("add value type: %w", err) } } handle, err := NewHandle(&b) if err != nil { // Check for 'full' map BTF support, since kernels between 4.18 and 5.2 // already support BTF blobs for maps without Var or Datasec just fine. if err := haveMapBTF(); err != nil { return nil, 0, 0, err } } return handle, keyID, valueID, err } golang-github-cilium-ebpf-0.21.0+ds1/btf/marshal_test.go000066400000000000000000000150611520243672000227720ustar00rootroot00000000000000package btf import ( "math" "testing" "github.com/go-quicktest/qt" "github.com/google/go-cmp/cmp" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestBuilderMarshal(t *testing.T) { typ := &Int{ Name: "foo", Size: 2, Encoding: Signed | Char, } want := []Type{ (*Void)(nil), typ, &Pointer{typ}, &Typedef{"baz", typ, nil}, } b, err := NewBuilder(want, nil) qt.Assert(t, qt.IsNil(err)) cpy := *b buf, err := b.Marshal(nil, &MarshalOptions{Order: internal.NativeEndian}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.CmpEquals(b, &cpy, cmp.AllowUnexported(*b)), qt.Commentf("Marshaling should not change Builder state")) have, err := loadRawSpec(buf, nil) qt.Assert(t, qt.IsNil(err), qt.Commentf("Couldn't parse BTF")) qt.Assert(t, qt.DeepEquals(typesFromSpec(t, have), want)) } func TestBuilderAdd(t *testing.T) { i := &Int{ Name: "foo", Size: 2, Encoding: Signed | Char, } pi := &Pointer{i} var b Builder id, err := b.Add(pi) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(id, TypeID(1)), qt.Commentf("First non-void type doesn't get id 1")) id, err = b.Add(pi) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(id, TypeID(1))) id, err = b.Add(i) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(id, TypeID(2)), qt.Commentf("Second type doesn't get id 2")) id, err = b.Add(i) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(id, TypeID(2)), qt.Commentf("Adding a type twice returns different ids")) id, err = b.Add(&Typedef{"baz", i, nil}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(id, TypeID(3))) } func TestBuilderSpec(t *testing.T) { b, err := NewBuilder([]Type{ &Int{Name: "foo", Size: 2}, &Int{Name: "foo", Size: 2}, }, &BuilderOptions{Deduplicate: true}) qt.Assert(t, qt.IsNil(err)) spec, err := b.Spec() qt.Assert(t, qt.IsNil(err)) // With deduplication enabled, both ints should be merged into one, // allowing queries with AnyTypeByName. _, err = spec.AnyTypeByName("foo") qt.Assert(t, qt.IsNil(err)) } func TestRoundtripVMlinux(t *testing.T) { types := typesFromSpec(t, vmlinuxSpec(t)) // Randomize the order to force different permutations of walking the type // graph. Keep Void at index 0. testutils.Rand(t).Shuffle(len(types[1:]), func(i, j int) { types[i+1], types[j+1] = types[j+1], types[i+1] }) visited := make(map[Type]struct{}) limitTypes: for i, typ := range types { for range postorder(typ, visited) { } if len(visited) >= math.MaxInt16 { // IDs exceeding math.MaxUint16 can trigger a bug when loading BTF. // This can be removed once the patch lands. // See https://lore.kernel.org/bpf/20220909092107.3035-1-oss@lmb.io/ types = types[:i] break limitTypes } } b, err := NewBuilder(types, nil) qt.Assert(t, qt.IsNil(err)) buf, err := b.Marshal(nil, KernelMarshalOptions()) qt.Assert(t, qt.IsNil(err)) rebuilt, err := loadRawSpec(buf, nil) qt.Assert(t, qt.IsNil(err), qt.Commentf("round tripping BTF failed")) if n := len(rebuilt.offsets); n > math.MaxUint16 { t.Logf("Rebuilt BTF contains %d types which exceeds uint16, test may fail on older kernels", n) } h, err := NewHandleFromRawBTF(buf) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err), qt.Commentf("loading rebuilt BTF failed")) h.Close() } func TestMarshalEnum64(t *testing.T) { enum := &Enum{ Name: "enum64", Size: 8, Signed: true, Values: []EnumValue{ {"A", 0}, {"B", 1}, }, } b, err := NewBuilder([]Type{enum}, nil) qt.Assert(t, qt.IsNil(err)) buf, err := b.Marshal(nil, &MarshalOptions{ Order: internal.NativeEndian, ReplaceEnum64: true, }) qt.Assert(t, qt.IsNil(err)) spec, err := loadRawSpec(buf, nil) qt.Assert(t, qt.IsNil(err)) var have *Union err = spec.TypeByName("enum64", &have) qt.Assert(t, qt.IsNil(err)) placeholder := &Int{Name: "enum64_placeholder", Size: 8, Encoding: Signed} qt.Assert(t, qt.DeepEquals(have, &Union{ Name: "enum64", Size: 8, Members: []Member{ {Name: "A", Type: placeholder}, {Name: "B", Type: placeholder}, }, })) } func TestMarshalDeclTags(t *testing.T) { types := []Type{ // Instead of an adjacent declTag, this will receive a placeholder Int. &Typedef{ Name: "decl tag typedef", Tags: []string{"decl tag"}, Type: &Int{Name: "decl tag target"}, }, } b, err := NewBuilder(types, nil) qt.Assert(t, qt.IsNil(err)) buf, err := b.Marshal(nil, &MarshalOptions{ Order: internal.NativeEndian, ReplaceDeclTags: true, }) qt.Assert(t, qt.IsNil(err)) spec, err := loadRawSpec(buf, nil) qt.Assert(t, qt.IsNil(err)) var td *Typedef qt.Assert(t, qt.IsNil(spec.TypeByName("decl tag typedef", &td))) var ti *Int qt.Assert(t, qt.IsNil(spec.TypeByName("decl_tag_placeholder", &ti))) } func TestMarshalTypeTags(t *testing.T) { types := []Type{ // Instead of pointing to a TypeTag, this will point to an intermediary Const. &Typedef{ Name: "type tag typedef", Type: &TypeTag{ Value: "type tag", Type: &Pointer{ Target: &Int{Name: "type tag target"}, }, }, }, } b, err := NewBuilder(types, nil) qt.Assert(t, qt.IsNil(err)) buf, err := b.Marshal(nil, &MarshalOptions{ Order: internal.NativeEndian, ReplaceTypeTags: true, }) qt.Assert(t, qt.IsNil(err)) spec, err := loadRawSpec(buf, nil) qt.Assert(t, qt.IsNil(err)) var td *Typedef qt.Assert(t, qt.IsNil(spec.TypeByName("type tag typedef", &td))) qt.Assert(t, qt.Satisfies(td.Type, func(typ Type) bool { _, ok := typ.(*Const) return ok })) } func BenchmarkMarshaler(b *testing.B) { types := typesFromSpec(b, vmlinuxTestdataSpec(b))[:100] b.ReportAllocs() for b.Loop() { var b Builder for _, typ := range types { _, _ = b.Add(typ) } _, _ = b.Marshal(nil, nil) } } func BenchmarkBuildVmlinux(b *testing.B) { types := typesFromSpec(b, vmlinuxTestdataSpec(b)) b.ReportAllocs() for b.Loop() { var b Builder for _, typ := range types { _, _ = b.Add(typ) } _, _ = b.Marshal(nil, nil) } } func marshalNativeEndian(tb testing.TB, types []Type) []byte { tb.Helper() b, err := NewBuilder(types, nil) qt.Assert(tb, qt.IsNil(err)) buf, err := b.Marshal(nil, nil) qt.Assert(tb, qt.IsNil(err)) return buf } func specFromTypes(tb testing.TB, types []Type) *Spec { tb.Helper() btf := marshalNativeEndian(tb, types) spec, err := loadRawSpec(btf, nil) qt.Assert(tb, qt.IsNil(err)) return spec } func typesFromSpec(tb testing.TB, spec *Spec) []Type { tb.Helper() types := make([]Type, 0, len(spec.offsets)) for typ, err := range spec.All() { qt.Assert(tb, qt.IsNil(err)) types = append(types, typ) } return types } golang-github-cilium-ebpf-0.21.0+ds1/btf/strings.go000066400000000000000000000112541520243672000217750ustar00rootroot00000000000000package btf import ( "bytes" "errors" "fmt" "io" "maps" "strings" "sync" ) // stringTable contains a sequence of null-terminated strings. // // It is safe for concurrent use. type stringTable struct { base *stringTable bytes []byte mu sync.Mutex cache map[uint32]string } // sizedReader is implemented by bytes.Reader, io.SectionReader, strings.Reader, etc. type sizedReader interface { io.Reader Size() int64 } func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) { bytes := make([]byte, r.Size()) if _, err := io.ReadFull(r, bytes); err != nil { return nil, err } return newStringTable(bytes, base) } func newStringTable(bytes []byte, base *stringTable) (*stringTable, error) { // When parsing split BTF's string table, the first entry offset is derived // from the last entry offset of the base BTF. firstStringOffset := uint32(0) if base != nil { firstStringOffset = uint32(len(base.bytes)) } if len(bytes) > 0 { if bytes[len(bytes)-1] != 0 { return nil, errors.New("string table isn't null terminated") } if firstStringOffset == 0 && bytes[0] != 0 { return nil, errors.New("first item in string table is non-empty") } } return &stringTable{base: base, bytes: bytes}, nil } func (st *stringTable) Lookup(offset uint32) (string, error) { // Fast path: zero offset is the empty string, looked up frequently. if offset == 0 { return "", nil } b, err := st.lookupSlow(offset) return string(b), err } func (st *stringTable) LookupBytes(offset uint32) ([]byte, error) { // Fast path: zero offset is the empty string, looked up frequently. if offset == 0 { return nil, nil } return st.lookupSlow(offset) } func (st *stringTable) lookupSlow(offset uint32) ([]byte, error) { if st.base != nil { n := uint32(len(st.base.bytes)) if offset < n { return st.base.lookupSlow(offset) } offset -= n } if offset > uint32(len(st.bytes)) { return nil, fmt.Errorf("offset %d is out of bounds of string table", offset) } if offset > 0 && st.bytes[offset-1] != 0 { return nil, fmt.Errorf("offset %d is not the beginning of a string", offset) } i := bytes.IndexByte(st.bytes[offset:], 0) return st.bytes[offset : offset+uint32(i)], nil } // LookupCache returns the string at the given offset, caching the result // for future lookups. func (cst *stringTable) LookupCached(offset uint32) (string, error) { // Fast path: zero offset is the empty string, looked up frequently. if offset == 0 { return "", nil } cst.mu.Lock() defer cst.mu.Unlock() if str, ok := cst.cache[offset]; ok { return str, nil } str, err := cst.Lookup(offset) if err != nil { return "", err } if cst.cache == nil { cst.cache = make(map[uint32]string) } cst.cache[offset] = str return str, nil } // stringTableBuilder builds BTF string tables. type stringTableBuilder struct { length uint32 strings map[string]uint32 } // newStringTableBuilder creates a builder with the given capacity. // // capacity may be zero. func newStringTableBuilder(capacity int) *stringTableBuilder { var stb stringTableBuilder if capacity == 0 { // Use the runtime's small default size. stb.strings = make(map[string]uint32) } else { stb.strings = make(map[string]uint32, capacity) } // Ensure that the empty string is at index 0. stb.append("") return &stb } // Add a string to the table. // // Adding the same string multiple times will only store it once. func (stb *stringTableBuilder) Add(str string) (uint32, error) { if strings.IndexByte(str, 0) != -1 { return 0, fmt.Errorf("string contains null: %q", str) } offset, ok := stb.strings[str] if ok { return offset, nil } return stb.append(str), nil } func (stb *stringTableBuilder) append(str string) uint32 { offset := stb.length stb.length += uint32(len(str)) + 1 stb.strings[str] = offset return offset } // Lookup finds the offset of a string in the table. // // Returns an error if str hasn't been added yet. func (stb *stringTableBuilder) Lookup(str string) (uint32, error) { offset, ok := stb.strings[str] if !ok { return 0, fmt.Errorf("string %q is not in table", str) } return offset, nil } // Length returns the length in bytes. func (stb *stringTableBuilder) Length() int { return int(stb.length) } // AppendEncoded appends the string table to the end of the provided buffer. func (stb *stringTableBuilder) AppendEncoded(buf []byte) []byte { n := len(buf) buf = append(buf, make([]byte, stb.Length())...) strings := buf[n:] for str, offset := range stb.strings { copy(strings[offset:], str) } return buf } // Copy the string table builder. func (stb *stringTableBuilder) Copy() *stringTableBuilder { return &stringTableBuilder{ stb.length, maps.Clone(stb.strings), } } golang-github-cilium-ebpf-0.21.0+ds1/btf/strings_test.go000066400000000000000000000052211520243672000230310ustar00rootroot00000000000000package btf import ( "bytes" "strings" "testing" "github.com/go-quicktest/qt" ) func TestStringTable(t *testing.T) { const in = "\x00one\x00two\x00" const splitIn = "three\x00four\x00" st, err := readStringTable(strings.NewReader(in), nil) if err != nil { t.Fatal(err) } // Parse string table of split BTF split, err := readStringTable(strings.NewReader(splitIn), st) if err != nil { t.Fatal(err) } testcases := []struct { offset uint32 want string }{ {0, ""}, {1, "one"}, {5, "two"}, {9, "three"}, {15, "four"}, } for _, tc := range testcases { have, err := split.Lookup(tc.offset) if err != nil { t.Errorf("Offset %d: %s", tc.offset, err) continue } if have != tc.want { t.Errorf("Offset %d: want %s but have %s", tc.offset, tc.want, have) } } if _, err := st.Lookup(2); err == nil { t.Error("No error when using offset pointing into middle of string") } // Make sure we reject bogus tables _, err = readStringTable(strings.NewReader("\x00one"), nil) if err == nil { t.Fatal("Accepted non-terminated string") } _, err = readStringTable(strings.NewReader("one\x00"), nil) if err == nil { t.Fatal("Accepted non-empty first item") } } func TestEmptyStringTable(t *testing.T) { empty, err := newStringTable(nil, nil) qt.Assert(t, qt.IsNil(err)) str, err := empty.Lookup(0) qt.Assert(t, qt.IsNil(err), qt.Commentf("Can't lookup empty string")) qt.Assert(t, qt.Equals(str, ""), qt.Commentf("Empty string lookup returned %q", str)) _, err = empty.Lookup(1) qt.Assert(t, qt.IsNotNil(err)) } func TestStringTableBuilder(t *testing.T) { stb := newStringTableBuilder(0) _, err := readStringTable(bytes.NewReader(stb.AppendEncoded(nil)), nil) qt.Assert(t, qt.IsNil(err), qt.Commentf("Can't parse string table")) _, err = stb.Add("foo\x00bar") qt.Assert(t, qt.IsNotNil(err)) empty, err := stb.Add("") qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(empty, 0), qt.Commentf("The empty string is not at index 0")) foo1, _ := stb.Add("foo") foo2, _ := stb.Add("foo") qt.Assert(t, qt.Equals(foo1, foo2), qt.Commentf("Adding the same string returns different offsets")) table := stb.AppendEncoded(nil) if n := bytes.Count(table, []byte("foo")); n != 1 { t.Fatalf("Marshalled string table contains foo %d times instead of once", n) } _, err = readStringTable(bytes.NewReader(table), nil) qt.Assert(t, qt.IsNil(err), qt.Commentf("Can't parse string table")) } func BenchmarkStringTableZeroLookup(b *testing.B) { strings := vmlinuxTestdataSpec(b).strings for b.Loop() { s, err := strings.Lookup(0) if err != nil { b.Fatal(err) } if s != "" { b.Fatal("0 is not the empty string") } } } golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/000077500000000000000000000000001520243672000215635ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/bpf_core_read.h000066400000000000000000000437671520243672000245270ustar00rootroot00000000000000/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ #ifndef __BPF_CORE_READ_H__ #define __BPF_CORE_READ_H__ /* * enum bpf_field_info_kind is passed as a second argument into * __builtin_preserve_field_info() built-in to get a specific aspect of * a field, captured as a first argument. __builtin_preserve_field_info(field, * info_kind) returns __u32 integer and produces BTF field relocation, which * is understood and processed by libbpf during BPF object loading. See * selftests/bpf for examples. */ enum bpf_field_info_kind { BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ BPF_FIELD_BYTE_SIZE = 1, BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ BPF_FIELD_SIGNED = 3, BPF_FIELD_LSHIFT_U64 = 4, BPF_FIELD_RSHIFT_U64 = 5, }; /* second argument to __builtin_btf_type_id() built-in */ enum bpf_type_id_kind { BPF_TYPE_ID_LOCAL = 0, /* BTF type ID in local program */ BPF_TYPE_ID_TARGET = 1, /* BTF type ID in target kernel */ }; /* second argument to __builtin_preserve_type_info() built-in */ enum bpf_type_info_kind { BPF_TYPE_EXISTS = 0, /* type existence in target kernel */ BPF_TYPE_SIZE = 1, /* type size in target kernel */ BPF_TYPE_MATCHES = 2, /* type match in target kernel */ }; /* second argument to __builtin_preserve_enum_value() built-in */ enum bpf_enum_value_kind { BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */ BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */ }; #define __CORE_RELO(src, field, info) \ __builtin_preserve_field_info((src)->field, BPF_FIELD_##info) #if __BYTE_ORDER == __LITTLE_ENDIAN #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst, \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #else /* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so * for big-endian we need to adjust destination pointer accordingly, based on * field byte size */ #define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ bpf_probe_read_kernel( \ (void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \ __CORE_RELO(src, fld, BYTE_SIZE), \ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) #endif /* * Extract bitfield, identified by s->field, and return its value as u64. * All this is done in relocatable manner, so bitfield changes such as * signedness, bit size, offset changes, this will be handled automatically. * This version of macro is using bpf_probe_read_kernel() to read underlying * integer storage. Macro functions as an expression and its return type is * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error. */ #define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \ unsigned long long val = 0; \ \ __CORE_BITFIELD_PROBE_READ(&val, s, field); \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) /* * Extract bitfield, identified by s->field, and return its value as u64. * This version of macro is using direct memory reads and should be used from * BPF program types that support such functionality (e.g., typed raw * tracepoints). */ #define BPF_CORE_READ_BITFIELD(s, field) ({ \ const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \ unsigned long long val; \ \ /* This is a so-called barrier_var() operation that makes specified \ * variable "a black box" for optimizing compiler. \ * It forces compiler to perform BYTE_OFFSET relocation on p and use \ * its calculated value in the switch below, instead of applying \ * the same relocation 4 times for each individual memory load. \ */ \ asm volatile("" : "=r"(p) : "0"(p)); \ \ switch (__CORE_RELO(s, field, BYTE_SIZE)) { \ case 1: val = *(const unsigned char *)p; break; \ case 2: val = *(const unsigned short *)p; break; \ case 4: val = *(const unsigned int *)p; break; \ case 8: val = *(const unsigned long long *)p; break; \ } \ val <<= __CORE_RELO(s, field, LSHIFT_U64); \ if (__CORE_RELO(s, field, SIGNED)) \ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ else \ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ val; \ }) /* * Convenience macro to check that field actually exists in target kernel's. * Returns: * 1, if matching field is present in target kernel; * 0, if no matching field found. */ #define bpf_core_field_exists(field) \ __builtin_preserve_field_info(field, BPF_FIELD_EXISTS) /* * Convenience macro to get the byte size of a field. Works for integers, * struct/unions, pointers, arrays, and enums. */ #define bpf_core_field_size(field) \ __builtin_preserve_field_info(field, BPF_FIELD_BYTE_SIZE) /* * Convenience macro to get BTF type ID of a specified type, using a local BTF * information. Return 32-bit unsigned integer with type ID from program's own * BTF. Always succeeds. */ #define bpf_core_type_id_local(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL) /* * Convenience macro to get BTF type ID of a target kernel's type that matches * specified local type. * Returns: * - valid 32-bit unsigned type ID in kernel BTF; * - 0, if no matching type was found in a target kernel BTF. */ #define bpf_core_type_id_kernel(type) \ __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET) /* * Convenience macro to check that provided named type * (struct/union/enum/typedef) exists in a target kernel. * Returns: * 1, if such type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_exists(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS) /* * Convenience macro to check that provided named type * (struct/union/enum/typedef) "matches" that in a target kernel. * Returns: * 1, if the type matches in the target kernel's BTF; * 0, if the type does not match any in the target kernel */ #define bpf_core_type_matches(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES) /* * Convenience macro to get the byte size of a provided named type * (struct/union/enum/typedef) in a target kernel. * Returns: * >= 0 size (in bytes), if type is present in target kernel's BTF; * 0, if no matching type is found. */ #define bpf_core_type_size(type) \ __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE) /* * Convenience macro to check that provided enumerator value is defined in * a target kernel. * Returns: * 1, if specified enum type and its enumerator value are present in target * kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value_exists(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS) /* * Convenience macro to get the integer value of an enumerator value in * a target kernel. * Returns: * 64-bit value, if specified enum type and its enumerator value are * present in target kernel's BTF; * 0, if no matching enum and/or enum value within that enum is found. */ #define bpf_core_enum_value(enum_type, enum_value) \ __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE) /* * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures * offset relocation for source address using __builtin_preserve_access_index() * built-in, provided by Clang. * * __builtin_preserve_access_index() takes as an argument an expression of * taking an address of a field within struct/union. It makes compiler emit * a relocation, which records BTF type ID describing root struct/union and an * accessor string which describes exact embedded field that was used to take * an address. See detailed description of this relocation format and * semantics in comments to struct bpf_field_reloc in libbpf_internal.h. * * This relocation allows libbpf to adjust BPF instruction to use correct * actual field offset, based on target kernel BTF type that matches original * (local) BTF, used to record relocation. */ #define bpf_core_read(dst, sz, src) \ bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user(dst, sz, src) \ bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str() * additionally emitting BPF CO-RE field relocation for specified source * argument. */ #define bpf_core_read_str(dst, sz, src) \ bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) /* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define bpf_core_read_user_str(dst, sz, src) \ bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) #define ___concat(a, b) a ## b #define ___apply(fn, n) ___concat(fn, n) #define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N /* * return number of provided arguments; used for switch-based variadic macro * definitions (see ___last, ___arrow, etc below) */ #define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) /* * return 0 if no arguments are passed, N - otherwise; used for * recursively-defined macros to specify termination (0) case, and generic * (N) case (e.g., ___read_ptrs, ___core_read) */ #define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0) #define ___last1(x) x #define ___last2(a, x) x #define ___last3(a, b, x) x #define ___last4(a, b, c, x) x #define ___last5(a, b, c, d, x) x #define ___last6(a, b, c, d, e, x) x #define ___last7(a, b, c, d, e, f, x) x #define ___last8(a, b, c, d, e, f, g, x) x #define ___last9(a, b, c, d, e, f, g, h, x) x #define ___last10(a, b, c, d, e, f, g, h, i, x) x #define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___nolast2(a, _) a #define ___nolast3(a, b, _) a, b #define ___nolast4(a, b, c, _) a, b, c #define ___nolast5(a, b, c, d, _) a, b, c, d #define ___nolast6(a, b, c, d, e, _) a, b, c, d, e #define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f #define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g #define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h #define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i #define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___arrow1(a) a #define ___arrow2(a, b) a->b #define ___arrow3(a, b, c) a->b->c #define ___arrow4(a, b, c, d) a->b->c->d #define ___arrow5(a, b, c, d, e) a->b->c->d->e #define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f #define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g #define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h #define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i #define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j #define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) #define ___type(...) typeof(___arrow(__VA_ARGS__)) #define ___read(read_fn, dst, src_type, src, accessor) \ read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) /* "recursively" read a sequence of inner pointers using local __t var */ #define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a); #define ___rd_last(fn, ...) \ ___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); #define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__) #define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) #define ___read_ptrs(fn, src, ...) \ ___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__) #define ___core_read0(fn, fn_ptr, dst, src, a) \ ___read(fn, dst, ___type(src), src, a); #define ___core_readN(fn, fn_ptr, dst, src, ...) \ ___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__)) \ ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \ ___last(__VA_ARGS__)); #define ___core_read(fn, fn_ptr, dst, src, a, ...) \ ___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst, \ src, a, ##__VA_ARGS__) /* * BPF_CORE_READ_INTO() is a more performance-conscious variant of * BPF_CORE_READ(), in which final field is read into user-provided storage. * See BPF_CORE_READ() below for more details on general usage. */ #define BPF_CORE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_INTO() */ #define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read, bpf_probe_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_USER_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as * BPF_CORE_READ() for intermediate pointers, but then executes (and returns * corresponding error code) bpf_core_read_str() for final string read. */ #define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_str, bpf_core_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory. * * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ #define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_core_read_user_str, bpf_core_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */ #define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_str, bpf_probe_read, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ ___core_read(bpf_probe_read_user_str, bpf_probe_read_user, \ dst, (src), a, ##__VA_ARGS__) \ }) /* * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially * when there are few pointer chasing steps. * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like: * int x = s->a.b.c->d.e->f->g; * can be succinctly achieved using BPF_CORE_READ as: * int x = BPF_CORE_READ(s, a.b.c, d.e, f, g); * * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF * CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically * equivalent to: * 1. const void *__t = s->a.b.c; * 2. __t = __t->d.e; * 3. __t = __t->f; * 4. return __t->g; * * Equivalence is logical, because there is a heavy type casting/preservation * involved, as well as all the reads are happening through * bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to * emit CO-RE relocations. * * N.B. Only up to 9 "field accessors" are supported, which should be more * than enough for any practical purpose. */ #define BPF_CORE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Variant of BPF_CORE_READ() for reading from user-space memory. * * NOTE: all the source types involved are still *kernel types* and need to * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will * fail. Custom user types are not relocatable with CO-RE. * The typical situation in which BPF_CORE_READ_USER() might be used is to * read kernel UAPI types from the user-space memory passed in as a syscall * input argument. */ #define BPF_CORE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* Non-CO-RE variant of BPF_CORE_READ() */ #define BPF_PROBE_READ(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) /* * Non-CO-RE variant of BPF_CORE_READ_USER(). * * As no CO-RE relocations are emitted, source types can be arbitrary and are * not restricted to kernel types only. */ #define BPF_PROBE_READ_USER(src, a, ...) ({ \ ___type((src), a, ##__VA_ARGS__) __r; \ BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ __r; \ }) #endif golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/btf_testmod.btf000066400000000000000000000400041520243672000245700ustar00rootroot00000000000000l&l&Nhl@pthl@p @ 1@9 9,;@AI6UZb@jrz@@@@@ )2@;DM V@ _ h q z@    @    8 :@ BB@B( A>@. BG @LORD  UE XhF Hkp@uXpJ H@G p k0 O  !0G K@O`lp@T[UV W2 TZ[ \3 Y_` W ^  b7ef g4 dcjk  vmn ipq l  @ @) + @-ErI Es=a||z|H'?-U7-U< -U=0 -U?R -UCC<7OPNMLKIG/t}{Q5rRSz  T W  T  W  T g%  T  \S ~  (  T WS  T  W  T g  T  \ (  7 : W  7 a W  7 z W   W   W   W  W  W 7 W Y W   v W   W   W  W   )+ W    W    W  )+ + W  @P W  s~ W  W   W   W   W  W  +9 W  Zp^ W  Zp W  Z W  W   W   W   W  < W  T W  Ery W  W  W   W    W   AL W  $ !W " Q $W % { 'W (  *W + W - @ /W 0  A 2W 3 2W 5 2W 7A 2W 9 ` ;W <{ *W >  @W A 2 2 2 # FW G : IW JR c  )+|~  N   )+|~ R  4 J 2k W X W Z  \  ^   `1 F b  E e      " l5 H  [ p E|~ r E t r E w  yW z 16hlp? | s)U ~ s)s  )s  r)+|s     )+|~u   )+|~u'  )+|~v E  w)c  x  |   |     . O d p ~           L  sxU}  sxU    +;A FMT     \ .$    ,) *!#  %'(+& 10 g T l T quly  ~ ~g lyg     )  g g  " bpf_testmod_test_read_ctxbufofflenbpf_testmod_test_write_ctxbpf_testmod_test_writable_ctxearly_retvalbpf_iter_testmod_seqvaluecntabbpf_testmod_opstest_1test_2test_maybe_nullunsupported_opstest_refcountedtest_return_ref_kptronebyteunsupporteddatatramp_1tramp_2tramp_3tramp_4tramp_5tramp_6tramp_7tramp_8tramp_9tramp_10tramp_11tramp_12tramp_13tramp_14tramp_15tramp_16tramp_17tramp_18tramp_19tramp_20tramp_21tramp_22tramp_23tramp_24tramp_25tramp_26tramp_27tramp_28tramp_29tramp_30tramp_31tramp_32tramp_33tramp_34tramp_35tramp_36tramp_37tramp_38tramp_39tramp_40bpf_testmod_ops2bpf_testmod_ops3st_ops_argsbpf_testmod_st_opstest_prologuetest_epiloguetest_pro_epilogueownerbpf_testmod_multi_st_opsnodeidx3x2x1prog_test_pass1x0arr2arr3prog_test_pass2arr1xprog_test_fail1pprog_test_fail2x8prog_test_fail3init_sock_argsaftypeaddr_argsaddraddrlensendmsg_argsmsgmsglenbpf_testmod_ctxrcuusagetrace_event_raw_bpf_testmod_test_readentpidcomm__datatrace_event_data_offsets_bpf_testmod_test_readbtf_trace_bpf_testmod_test_readeventhandlerbtf_trace_bpf_testmod_test_write_bare_tpbtf_trace_bpf_testmod_test_nullable_bare_tpbtf_trace_bpf_testmod_test_raw_tp_null_tpbtf_trace_bpf_testmod_test_writable_bare_tpfunc_proto_typedeffunc_proto_typedef_nested1func_proto_typedef_nested2bpf_testmod_struct_arg_1bpf_testmod_struct_arg_2bpf_testmod_struct_arg_3bpf_testmod_struct_arg_4bpf_testmod_struct_arg_5cdbpf_testmod_union_arg_1argbpf_testmod_union_arg_2bpf_testmod_btf_type_tag_1bpf_testmod_btf_type_tag_2bpf_testmod_btf_type_tag_3testmod_uprobeconsumerbpf_struct_ops_bpf_testmod_opscommonbpf_struct_ops_bpf_testmod_ops2bpf_struct_ops_bpf_testmod_ops3bpf_struct_ops_bpf_testmod_st_opsbpf_struct_ops_bpf_testmod_multi_st_opsbpf_testmod_ksym_percpuctx__nullable__bpf_trace_bpf_testmod_test_nullable_bare_tp__bpf_trace_bpf_testmod_test_raw_tp_null_tptaskctx__bpf_trace_bpf_testmod_test_read__bpf_trace_bpf_testmod_test_writable_bare_tp__bpf_trace_bpf_testmod_test_write_bare_tp__probestub_bpf_testmod_test_nullable_bare_tp__probestub_bpf_testmod_test_raw_tp_null_tp__probestub_bpf_testmod_test_read__probestub_bpf_testmod_test_writable_bare_tp__probestub_bpf_testmod_test_write_bare_tp__traceiter_bpf_testmod_test_nullable_bare_tp__traceiter_bpf_testmod_test_raw_tp_null_tp__traceiter_bpf_testmod_test_read__traceiter_bpf_testmod_test_writable_bare_tp__traceiter_bpf_testmod_test_write_bare_tpbpf_dummy_reg2itbpf_iter_testmod_seq_destroybpf_kfuncbpf_iter_testmod_seq_newbpf_iter_testmod_seq_nextit__iterbpf_iter_testmod_seq_valuebpf_kfunc_call_int_mem_releaseargsbpf_kfunc_call_kernel_bindbpf_kfunc_call_kernel_connectbpf_kfunc_call_kernel_getpeernamebpf_kfunc_call_kernel_getsocknamebpf_kfunc_call_kernel_listenbpf_kfunc_call_kernel_sendmsgbpf_kfunc_call_memb1_releasebpf_kfunc_call_memb_acquirebpf_kfunc_call_sock_sendmsgskbpf_kfunc_call_test1bpf_kfunc_call_test2bpf_kfunc_call_test3bpf_kfunc_call_test4rdonly_buf_sizebpf_kfunc_call_test_acq_rdonly_memscalar_ptrbpf_kfunc_call_test_acquirebpf_kfunc_call_test_destructivebpf_kfunc_call_test_fail1bpf_kfunc_call_test_fail2bpf_kfunc_call_test_fail3bpf_kfunc_call_test_get_rdonly_memrdwr_buf_sizebpf_kfunc_call_test_get_rdwr_memmembpf_kfunc_call_test_mem_len_fail1bpf_kfunc_call_test_mem_len_fail2mem__szbpf_kfunc_call_test_mem_len_pass1bpf_kfunc_call_test_offsetbpf_kfunc_call_test_pass1bpf_kfunc_call_test_pass2skbbpf_kfunc_call_test_pass_ctxbpf_kfunc_call_test_refbpf_kfunc_call_test_sleepableunusedbpf_kfunc_call_test_static_unused_argbpf_kfunc_close_sockbpf_kfunc_common_testptrptr__nullablebpf_kfunc_dynptr_testbpf_kfunc_init_sockbpf_kfunc_multi_st_ops_test_1bpf_kfunc_nested_acquire_nonzero_offset_testbpf_kfunc_nested_acquire_zero_offset_testbpf_kfunc_nested_release_testbpf_kfunc_rcu_task_testbpf_kfunc_ret_rcu_testbpf_kfunc_ret_rcu_test_nostructbpf_kfunc_st_ops_inc10bpf_kfunc_st_ops_test_epiloguebpf_kfunc_st_ops_test_pro_epiloguebpf_kfunc_st_ops_test_prologuebpf_kfunc_trusted_num_testbpf_kfunc_trusted_task_testbpf_kfunc_trusted_vma_testbpf_test_mod_st_ops__test_epiloguebpf_test_mod_st_ops__test_pro_epiloguebpf_test_mod_st_ops__test_prologueerrbpf_testmod_ctx_createbpf_testmod_ctx_releasebpf_testmod_exitbpf_testmod_fentry_test1efghijkbpf_testmod_fentry_test11bpf_testmod_fentry_test2bpf_testmod_fentry_test3bpf_testmod_fentry_test7bpf_testmod_initbpf_testmod_looooooooooooooooooooooooooooooong_namebpf_testmod_loop_testbpf_testmod_multi_st_ops__test_1bpf_testmod_ops3_call_test_1bpf_testmod_ops3_call_test_2dummytask__nullablebpf_testmod_ops__test_maybe_nulltask__refbpf_testmod_ops__test_refcountedcgrpbpf_testmod_ops__test_return_ref_kptrbpf_testmod_ops_initbpf_testmod_ops_init_memberbpf_testmod_ops_is_valid_accessbpf_testmod_return_ptrbpf_testmod_stacktrace_testbpf_testmod_stacktrace_test_1bpf_testmod_stacktrace_test_2bpf_testmod_stacktrace_test_3bpf_testmod_test_1bpf_testmod_test_2bpf_testmod_test_3bpf_testmod_test_4bpf_testmod_test_arg_ptr_to_structbpf_testmod_test_btf_type_tag_percpu_1bpf_testmod_test_btf_type_tag_percpu_2bpf_testmod_test_btf_type_tag_user_1bpf_testmod_test_btf_type_tag_user_2bpf_testmod_test_mod_kfunckobjbin_attrbpf_testmod_test_readbpf_testmod_test_struct_arg_1bpf_testmod_test_struct_arg_2bpf_testmod_test_struct_arg_3bpf_testmod_test_struct_arg_4bpf_testmod_test_struct_arg_5bpf_testmod_test_struct_arg_6bpf_testmod_test_struct_arg_7bpf_testmod_test_struct_arg_8bpf_testmod_test_struct_arg_9bpf_testmod_test_union_arg_1bpf_testmod_test_union_arg_2bpf_testmod_test_writebpf_testmod_trampbpf_testmod_uprobe_writemulti_st_ops_initmulti_st_ops_init_membermulti_st_ops_regmulti_st_ops_unregperf_trace_bpf_testmod_test_readst_ops3_check_memberst_ops3_regst_ops3_unregst_ops_btf_struct_accessst_ops_gen_epiloguest_ops_gen_prologuest_ops_initst_ops_init_memberst_ops_regst_ops_unregtest_1_recursion_detectedtestmod_free_cbtrace_event_raw_event_bpf_testmod_test_readtrace_raw_output_bpf_testmod_test_readselfregsuprobe_handlerfuncuprobe_ret_handler.data..percpu__s16__u32__s64__u64s16u32s64u64__kernel_long_t__kernel_ulong_t__kernel_pid_t__kernel_size_t__kernel_ssize_t__kernel_loff_tpid_tboolloff_tsize_tssize_trefcount_tprogheadlogregsizetmemberkdataudatainfolinkinsn_bufdirect_writectx_stack_offiterflagsngolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/btf_testmod.btf.base000066400000000000000000000023321520243672000255030ustar00rootroot00000000000000ttN@ %1; ?@M@d@ms~ @@H  $`-=(QPZf@u 0(p): long unsigned intcharunsigned intsigned charshort intintlong long intlong long unsigned intlong int_Boolhlist_nodecallback_headtask_structpt_regsmodulerefcount_structbpf_raw_event_mapfilevm_area_structsk_buffpathcgroupkobjectbin_attributebpf_insnbtf_typebtf_memberbpf_progbpf_access_typebpf_insn_access_auxbpf_linktrace_entrytrace_iteratorprint_line_ttrace_eventsocksk_buff_headsock_commonuprobe_consumerbpf_dynptrbpf_verifier_logbpf_reg_state__sk_buffbtfbpf_struct_ops_common_valueuprobeprog_test_member1prog_test_memberprog_test_ref_kfuncgolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/fuzz/000077500000000000000000000000001520243672000225615ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/fuzz/FuzzExtInfo/000077500000000000000000000000001520243672000250145ustar00rootroot0000000000000050a33736610b4a0945179db4c8a88e8247b05fbb25f50ed81e5393baf29bc5bc000066400000000000000000000002171520243672000354550ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/fuzz/FuzzExtInfogo test fuzz v1 []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") []byte("0") 72534f53bd90cb52a017013499b11511535c1295bf0e22f856148c02454c323e000066400000000000000000000001641520243672000346260ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/fuzz/FuzzExtInfogo test fuzz v1 []byte("\x9f\xeb\x01\x00\x18\x00\x00\x00\x00\x00\x00\x000000000000000\x00\x00\x000000") []byte("0") a87a26efa64ed50b598ae8e333301d57d5f234527730f042d68ccc736e90c9fa000066400000000000000000000002001520243672000354640ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/fuzz/FuzzExtInfogo test fuzz v1 []byte("\x9f\xeb\x01\x00\x1c\x00\x00\x00\x00\x00\x00\x000000\xe8\xff\xff\xff000000000\x00\x00\x00") []byte("0") golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs-eb.elf000066400000000000000000000364701520243672000241400ustar00rootroot00000000000000ELF9@@/a0]1Y2U4Q5 M6 I7 E8 A9 =: 9; 5< 1=->)?%F !G  !J  !K  ! N  ! O!lXmUnSoQpNqKrIsGtDuAv?w=z U: { U7 |U5 }U3 ~U0U-U+U)U&U#U!U  #!UUU U U * c!s!c!۷  s* c!s!c!ܷkݷh޷e* ܷc!s!c!  \* c!s!c!UT* ķc!s!c! U K* c!s!c!UCU@U=U:U7U4U1.+(%"UUUUU U  U  U xUxxx #        @ @ '  F p  9     x    '  9  \   !   inttype_idssocket/type_ids/ebpf/btf/testdata/relocs.c__section("socket/type_ids") int type_ids() {0 local_id_not_zero(int);frob local_id_not_zero(struct { int frob; });FRAP local_id_not_zero(enum {FRAP});barchar local_id_not_zero(union { char bar; });s_1_2_3unsigned int local_id_not_zero(struct s);s_t local_id_not_zero(s_t); local_id_not_zero(const s_t); local_id_not_zero(volatile s_t);eZEROONETWO local_id_not_zero(enum e);e_t local_id_not_zero(e_t); local_id_not_zero(const e_t); local_id_not_zero(volatile e_t);u local_id_not_zero(union u);u_t local_id_not_zero(u_t); local_id_not_zero(const u_t); local_id_not_zero(volatile u_t); target_and_local_id_dont_match(struct s); target_and_local_id_dont_match(s_t); target_and_local_id_dont_match(enum e); target_and_local_id_dont_match(e_t); target_and_local_id_dont_match(union u);}typessocket/types__section("socket/types") int types() { type_exists(struct s); type_exists(s_t); type_exists(const s_t); type_exists(volatile s_t); type_exists(enum e); type_exists(e_t); type_exists(const e_t); type_exists(volatile e_t); type_exists(union u); type_exists(u_t); type_exists(const u_t); type_exists(volatile u_t); type_size_matches(struct s); type_size_matches(s_t); type_size_matches(const s_t); type_size_matches(volatile s_t); type_size_matches(enum e); type_size_matches(e_t); type_size_matches(const e_t); type_size_matches(volatile e_t); type_size_matches(union u); type_size_matches(u_t); type_size_matches(const u_t); type_size_matches(volatile u_t); type_matches(struct s); type_matches(s_t); type_matches(const s_t); type_matches(volatile s_t); type_matches(enum e); type_matches(e_t); type_matches(const e_t); type_matches(volatile e_t); type_matches(union u); type_matches(u_t); type_matches(const u_t);enumssocket/enums__section("socket/enums") int enums() {1 enum_value_exists(enum e, ONE); enum_value_exists(volatile enum e, ONE); enum_value_exists(const enum e, ONE);2 enum_value_exists(e_t, TWO);e64LARGE enum_value_exists(enum e64, LARGE); enum_value_matches(enum e, ZERO); enum_value_matches(enum e, TWO); enum_value_matches(e_t, ONE); enum_value_matches(volatile e_t, ONE); enum_value_matches(const e_t, ONE);fieldssocket/fields__section("socket/fields") int fields() {0:0 field_exists((struct s){}._1);0:10:2 field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); field_is_signed((struct s){}._1); field_is_unsigned((struct s){}._3); field_size_matches((struct s){}._1); field_size_matches((s_t){}._2); field_size_matches((union u){}._1); field_size_matches((u_t){}._2); field_offset_matches(struct s, _1); field_offset_matches(s_t, _2); field_offset_matches(union u, _1); field_offset_matches(u_t, _2);t__ARRAY_SIZE_TYPE__0:0:0:2:0 field_exists(bar.s[2]._1);0:0:0:1:1 field_exists(bar.s[1]._2);0:1:0:0 field_exists(bar.u._1);0:1:0:1 field_exists(bar.u._2);1:1:0:1 field_exists(barp[1].u._2); field_is_signed(bar.s[2]._1);0:0:0:2:2 field_is_unsigned(bar.s[2]._3); field_size_matches(bar.s[2]._1); field_size_matches(bar.s[1]._2); field_size_matches(bar.u._1); field_size_matches(bar.u._2); field_size_matches(barp[1].u._2); field_offset_matches(struct t, s[2]._1); field_offset_matches(struct t, s[1]._2); field_offset_matches(struct t, u._1);err_ambiguoussocket/err_ambiguousambiguous return bpf_core_type_id_kernel(struct ambiguous);err_ambiguous_flavoursocket/err_ambiguous_flavourambiguous___flavour return bpf_core_type_id_kernel(struct ambiguous___flavour); dd(v? G ":j8Xx"D]|8Xx)Jc(X(,D8(nPv%(8H` x!4Mi(8P@h\xu  -F Z$(t(@,X0h4x8< @DnT? Lv(8Xx7Z|8nh +lX Sp nt x  X   7 \ }  8 P h & L p     ( ;@ ]X | p $ , 0 4nD G fx  h(hHhhhh h h h h( hH hh h hhhhhh8 hH hh hx h h h h hhhvh hX hp h hhh  h H h ` h  h h h  h 8 h P h  h h ? t H  hh h  t h 0 '  K( O8 'X '` Kh Ox K ' K ' K O ' ' K  O0 OX '` Kh Ox ' ' K O K ' K ' K0 'H K` x B h       B8 hP h   B h G h #hX0=0@Jh,<L\l| 0@P`p(8HXhx(8HXhx(8H`p(8HXhx(8HXhx 8Tdt$4DTdt   , < L \ l |           , D T d t           , < L \ l |           , < L \ l |           , < L \ l |     .text.rel.BTF.extsocket/err_ambiguoussocket/enumssocket/typessocket/fieldssocket/type_idssocket/err_ambiguous_flavour.llvm_addrsig.strtab.symtab.BTF9@Q@06p)P@C xa     @+ ` ~oL9*x8golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs-el.elf000066400000000000000000000364701520243672000241520ustar00rootroot00000000000000ELF9@@/a0]1Y2U4Q5 M6 I7 E8 A9 =: 9; 5< 1=->)?%FG  J  K   N  OlXmUnSoQpNqKrIsGtDuAv?w=z U: { U7 |U5 }U3 ~U0U-U+U)U&U#U!U  #!UUU U U!cscscsckhecsc\cscUTcscUKcscUCU@U=U:U7U4U1.+(%"UUUUU U  U  U xUxxx #       @ @ '  F p  9     x     ' 9  \     !   inttype_idssocket/type_ids/ebpf/btf/testdata/relocs.c__section("socket/type_ids") int type_ids() {0 local_id_not_zero(int);frob local_id_not_zero(struct { int frob; });FRAP local_id_not_zero(enum {FRAP});barchar local_id_not_zero(union { char bar; });s_1_2_3unsigned int local_id_not_zero(struct s);s_t local_id_not_zero(s_t); local_id_not_zero(const s_t); local_id_not_zero(volatile s_t);eZEROONETWO local_id_not_zero(enum e);e_t local_id_not_zero(e_t); local_id_not_zero(const e_t); local_id_not_zero(volatile e_t);u local_id_not_zero(union u);u_t local_id_not_zero(u_t); local_id_not_zero(const u_t); local_id_not_zero(volatile u_t); target_and_local_id_dont_match(struct s); target_and_local_id_dont_match(s_t); target_and_local_id_dont_match(enum e); target_and_local_id_dont_match(e_t); target_and_local_id_dont_match(union u);}typessocket/types__section("socket/types") int types() { type_exists(struct s); type_exists(s_t); type_exists(const s_t); type_exists(volatile s_t); type_exists(enum e); type_exists(e_t); type_exists(const e_t); type_exists(volatile e_t); type_exists(union u); type_exists(u_t); type_exists(const u_t); type_exists(volatile u_t); type_size_matches(struct s); type_size_matches(s_t); type_size_matches(const s_t); type_size_matches(volatile s_t); type_size_matches(enum e); type_size_matches(e_t); type_size_matches(const e_t); type_size_matches(volatile e_t); type_size_matches(union u); type_size_matches(u_t); type_size_matches(const u_t); type_size_matches(volatile u_t); type_matches(struct s); type_matches(s_t); type_matches(const s_t); type_matches(volatile s_t); type_matches(enum e); type_matches(e_t); type_matches(const e_t); type_matches(volatile e_t); type_matches(union u); type_matches(u_t); type_matches(const u_t);enumssocket/enums__section("socket/enums") int enums() {1 enum_value_exists(enum e, ONE); enum_value_exists(volatile enum e, ONE); enum_value_exists(const enum e, ONE);2 enum_value_exists(e_t, TWO);e64LARGE enum_value_exists(enum e64, LARGE); enum_value_matches(enum e, ZERO); enum_value_matches(enum e, TWO); enum_value_matches(e_t, ONE); enum_value_matches(volatile e_t, ONE); enum_value_matches(const e_t, ONE);fieldssocket/fields__section("socket/fields") int fields() {0:0 field_exists((struct s){}._1);0:10:2 field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); field_is_signed((struct s){}._1); field_is_unsigned((struct s){}._3); field_size_matches((struct s){}._1); field_size_matches((s_t){}._2); field_size_matches((union u){}._1); field_size_matches((u_t){}._2); field_offset_matches(struct s, _1); field_offset_matches(s_t, _2); field_offset_matches(union u, _1); field_offset_matches(u_t, _2);t__ARRAY_SIZE_TYPE__0:0:0:2:0 field_exists(bar.s[2]._1);0:0:0:1:1 field_exists(bar.s[1]._2);0:1:0:0 field_exists(bar.u._1);0:1:0:1 field_exists(bar.u._2);1:1:0:1 field_exists(barp[1].u._2); field_is_signed(bar.s[2]._1);0:0:0:2:2 field_is_unsigned(bar.s[2]._3); field_size_matches(bar.s[2]._1); field_size_matches(bar.s[1]._2); field_size_matches(bar.u._1); field_size_matches(bar.u._2); field_size_matches(barp[1].u._2); field_offset_matches(struct t, s[2]._1); field_offset_matches(struct t, s[1]._2); field_offset_matches(struct t, u._1);err_ambiguoussocket/err_ambiguousambiguous return bpf_core_type_id_kernel(struct ambiguous);err_ambiguous_flavoursocket/err_ambiguous_flavourambiguous___flavour return bpf_core_type_id_kernel(struct ambiguous___flavour); dd(v?G  ":j8Xx"D]|8Xx)Jc(X(,D8(nPv%(8H` x!4Mi(8P@h\xu  -F Z$(t(@,X0h4x8< @DnT? Lv(8Xx7Z|8nh+ lXS pn t x  X  7 \ }   8 P h& L p      (; @] X|  p $ , 0 4nDG f x  h(hHhhhh h h h h( hH hh h hhhhhh8 hH hh hx h h h h hhhvh hX hp h hhh  h H h ` h  h h h  h 8 h P h  h h ? t H  hh h  t h 0' K (O 8' X ' ` K h O x K  ' K ' K O ' ' K O 0O X' `K hO x'  '  K  O  K  ' K '  K 0 ' HK ` xB h       B 8h P h  B h  G  h #hX0=0@Jh,<L\l| 0@P`p(8HXhx(8HXhx(8H`p(8HXhx(8HXhx 8Tdt$4DTdt  , < L \ l |           , D T d t           , < L \ l |           , < L \ l |           , < L \ l |      .text.rel.BTF.extsocket/err_ambiguoussocket/enumssocket/typessocket/fieldssocket/type_idssocket/err_ambiguous_flavour.llvm_addrsig.strtab.symtab.BTF9@Q@06p)P@Cx a     @+` ~Lo9x*8golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs.c000066400000000000000000000144031520243672000232200ustar00rootroot00000000000000#include "../../testdata/common.h" #include "bpf_core_read.h" enum e { ZERO = 0, ONE, TWO, }; enum e64 { LARGE = 0x1ffffffff, }; typedef enum e e_t; struct s { int _1; char _2; unsigned int _3; }; typedef struct s s_t; union u { int *_1; char *_2; unsigned int *_3; }; typedef union u u_t; #define local_id_not_zero(expr) \ ({ \ if (bpf_core_type_id_local(expr) == 0) { \ return __LINE__; \ } \ }) #define target_and_local_id_dont_match(expr) \ ({ \ if (bpf_core_type_id_kernel(expr) == bpf_core_type_id_local(expr)) { \ return __LINE__; \ } \ }) __section("socket/type_ids") int type_ids() { local_id_not_zero(int); local_id_not_zero(struct { int frob; }); local_id_not_zero(enum {FRAP}); local_id_not_zero(union { char bar; }); local_id_not_zero(struct s); local_id_not_zero(s_t); local_id_not_zero(const s_t); local_id_not_zero(volatile s_t); local_id_not_zero(enum e); local_id_not_zero(e_t); local_id_not_zero(const e_t); local_id_not_zero(volatile e_t); local_id_not_zero(union u); local_id_not_zero(u_t); local_id_not_zero(const u_t); local_id_not_zero(volatile u_t); // In this context, target is the BTF generated by clang. local is // generated on the fly by the library. There is a low chance that // the order on both is the same, so we assert this to make sure that // CO-RE uses the IDs from the dynamic BTF. // Qualifiers on types crash clang. target_and_local_id_dont_match(struct s); target_and_local_id_dont_match(s_t); // target_and_local_id_dont_match(const s_t); // target_and_local_id_dont_match(volatile s_t); target_and_local_id_dont_match(enum e); target_and_local_id_dont_match(e_t); // target_and_local_id_dont_match(const e_t); // target_and_local_id_dont_match(volatile e_t); target_and_local_id_dont_match(union u); target_and_local_id_dont_match(u_t); // target_and_local_id_dont_match(const u_t); // target_and_local_id_dont_match(volatile u_t); return 0; } #define type_exists(expr) \ ({ \ if (!bpf_core_type_exists(expr)) { \ return __LINE__; \ } \ }) #define type_size_matches(expr) \ ({ \ if (bpf_core_type_size(expr) != sizeof(expr)) { \ return __LINE__; \ } \ }) #define type_matches(expr) \ ({ \ if (!bpf_core_type_matches(expr)) { \ return __LINE__; \ } \ }) __section("socket/types") int types() { type_exists(struct s); type_exists(s_t); type_exists(const s_t); type_exists(volatile s_t); type_exists(enum e); type_exists(e_t); type_exists(const e_t); type_exists(volatile e_t); type_exists(union u); type_exists(u_t); type_exists(const u_t); type_exists(volatile u_t); // TODO: Check non-existence. type_size_matches(struct s); type_size_matches(s_t); type_size_matches(const s_t); type_size_matches(volatile s_t); type_size_matches(enum e); type_size_matches(e_t); type_size_matches(const e_t); type_size_matches(volatile e_t); type_size_matches(union u); type_size_matches(u_t); type_size_matches(const u_t); type_size_matches(volatile u_t); type_matches(struct s); type_matches(s_t); type_matches(const s_t); type_matches(volatile s_t); type_matches(enum e); type_matches(e_t); type_matches(const e_t); type_matches(volatile e_t); type_matches(union u); type_matches(u_t); type_matches(const u_t); type_matches(volatile u_t); return 0; } #define enum_value_exists(t, v) \ ({ \ if (!bpf_core_enum_value_exists(t, v)) { \ return __LINE__; \ } \ }) #define enum_value_matches(t, v) \ ({ \ if (v != bpf_core_enum_value(t, v)) { \ return __LINE__; \ } \ }) __section("socket/enums") int enums() { enum_value_exists(enum e, ONE); enum_value_exists(volatile enum e, ONE); enum_value_exists(const enum e, ONE); enum_value_exists(e_t, TWO); enum_value_exists(enum e64, LARGE); // TODO: Check non-existence. enum_value_matches(enum e, ZERO); enum_value_matches(enum e, TWO); enum_value_matches(e_t, ONE); enum_value_matches(volatile e_t, ONE); enum_value_matches(const e_t, ONE); enum_value_matches(enum e64, LARGE); return 0; } #define field_exists(f) \ ({ \ if (!bpf_core_field_exists(f)) { \ return __LINE__; \ } \ }) #define field_size_matches(f) \ ({ \ if (sizeof(f) != bpf_core_field_size(f)) { \ return __LINE__; \ } \ }) #define field_offset_matches(t, f) \ ({ \ if (__builtin_offsetof(t, f) != __builtin_preserve_field_info(((typeof(t) *)0)->f, BPF_FIELD_BYTE_OFFSET)) { \ return __LINE__; \ } \ }) #define field_is_signed(f) \ ({ \ if (!__builtin_preserve_field_info(f, BPF_FIELD_SIGNED)) { \ return __LINE__; \ } \ }) #define field_is_unsigned(f) \ ({ \ if (__builtin_preserve_field_info(f, BPF_FIELD_SIGNED)) { \ return __LINE__; \ } \ }) __section("socket/fields") int fields() { field_exists((struct s){}._1); field_exists((s_t){}._2); field_exists((union u){}._1); field_exists((u_t){}._2); field_is_signed((struct s){}._1); field_is_unsigned((struct s){}._3); // unions crash clang-14. // field_is_signed((union u){}._1); // field_is_unsigned((union u){}._3); field_size_matches((struct s){}._1); field_size_matches((s_t){}._2); field_size_matches((union u){}._1); field_size_matches((u_t){}._2); field_offset_matches(struct s, _1); field_offset_matches(s_t, _2); field_offset_matches(union u, _1); field_offset_matches(u_t, _2); struct t { union { s_t s[10]; }; struct { union u u; }; } bar, *barp = &bar; field_exists(bar.s[2]._1); field_exists(bar.s[1]._2); field_exists(bar.u._1); field_exists(bar.u._2); field_exists(barp[1].u._2); field_is_signed(bar.s[2]._1); field_is_unsigned(bar.s[2]._3); // unions crash clang-14. // field_is_signed(bar.u._1); // field_is_signed(bar.u._3); field_size_matches(bar.s[2]._1); field_size_matches(bar.s[1]._2); field_size_matches(bar.u._1); field_size_matches(bar.u._2); field_size_matches(barp[1].u._2); field_offset_matches(struct t, s[2]._1); field_offset_matches(struct t, s[1]._2); field_offset_matches(struct t, u._1); field_offset_matches(struct t, u._2); return 0; } struct ambiguous { int _1; char _2; }; struct ambiguous___flavour { char _1; int _2; }; __section("socket/err_ambiguous") int err_ambiguous() { return bpf_core_type_id_kernel(struct ambiguous); } __section("socket/err_ambiguous_flavour") int err_ambiguous_flavour() { return bpf_core_type_id_kernel(struct ambiguous___flavour); } golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_enum-eb.elf000066400000000000000000000031701520243672000251530ustar00rootroot00000000000000ELF8@@ cLL   &7F\intcore_ld64immsocket/core_ld64immcgroup_subsys_idcpuset_cgrp_idcpuset_cgrp_id_lublubCGROUP_SUBSYS_COUNT1/ebpf/btf/testdata/relocs_enum.c__section("socket/core_ld64imm") int core_ld64imm() { if (bpf_core_enum_value_exists(enum cgroup_subsys_id, cpuset_cgrp_id_lublub)) { __attribute__((unused)) const volatile int val = bpf_core_enum_value(enum cgroup_subsys_id, cpuset_cgrp_id_lublub); return 0; L`,r,r0(r4.0r<p p @,@P`p .text.rel.BTF.extsocket/core_ld64imm.llvm_addrsig.strtab.symtab.BTF6K@@@F  @xp(oL>0Hgolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_enum-el.elf000066400000000000000000000031701520243672000251650ustar00rootroot00000000000000ELF8@@ cLL   &7F\intcore_ld64immsocket/core_ld64immcgroup_subsys_idcpuset_cgrp_idcpuset_cgrp_id_lublubCGROUP_SUBSYS_COUNT1/ebpf/btf/testdata/relocs_enum.c__section("socket/core_ld64imm") int core_ld64imm() { if (bpf_core_enum_value_exists(enum cgroup_subsys_id, cpuset_cgrp_id_lublub)) { __attribute__((unused)) const volatile int val = bpf_core_enum_value(enum cgroup_subsys_id, cpuset_cgrp_id_lublub); return 0; L`,r,r0(r.40r<p p @,@P`p .text.rel.BTF.extsocket/core_ld64imm.llvm_addrsig.strtab.symtab.BTF6K@@@F  @xp(Lo>0Hgolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_enum.c000066400000000000000000000007021520243672000242410ustar00rootroot00000000000000#include "bpf_core_read.h" enum cgroup_subsys_id { cpuset_cgrp_id, cpuset_cgrp_id_lublub, CGROUP_SUBSYS_COUNT, }; #define __section(NAME) __attribute__((section(NAME), used)) __section("socket/core_ld64imm") int core_ld64imm() { if (bpf_core_enum_value_exists(enum cgroup_subsys_id, cpuset_cgrp_id_lublub)) { __attribute__((unused)) const volatile int val = bpf_core_enum_value(enum cgroup_subsys_id, cpuset_cgrp_id_lublub); } return 0; } golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_read-eb.elf000066400000000000000000000166301520243672000251270ustar00rootroot00000000000000ELF@@ k*q-qUD3"VV{* !0e00 0 i00yqag  <w<LU* !0e00 0 i00yqag$  >w>OU* !0e00 0 i00yqag'  >w>RUw* !0e00 0 i00yqag&  ?w?UU\* !0e00 0 i00yqag)  ?w?XUA* !0e00 0 i00yqag*  0w0[U&VV* !0e00 0 i00yqag  "w"^U D3"aUdUg   z|~~ |$ & ' ) * @  $)-1@=K W intread_subprog.text/ebpf/btf/testdata/relocs_read.c__attribute__((noinline)) int read_subprog() { struct s foo = {sbachar0:1 if (core_access(foo.a) == 0)0:0 if (core_access(foo.b) == 1) *p++ = 0xff; // a, b, dbitsxcdefgu8unsigned charu16unsigned shortunsigned intZEROONEu64unsigned long if (BPF_CORE_READ_BITFIELD(&bar, a) != (1 << 4) - 1)0:2 if (BPF_CORE_READ_BITFIELD(&bar, b) != (1 << 2) - 1)0:4 if (BPF_CORE_READ_BITFIELD(&bar, d) != (1 << 2) - 1)0:3 if (BPF_CORE_READ_BITFIELD(&bar, c) != 0)0:5 if (BPF_CORE_READ_BITFIELD(&bar, e) != 0)0:6 if (BPF_CORE_READ_BITFIELD(&bar, f) != 0x5656)0:7 if (BPF_CORE_READ_BITFIELD(&bar, g) != 0x15443322)nonexistnon_exist0 if (bpf_core_type_exists(struct nonexist) != 0) if (bpf_core_field_exists(((struct nonexist *)0)->non_exist) != 0)nonexist_enumNON_EXIST}readssocket int ret = read_subprog(); $$];9h (0PXp?,?,?,?,?, ?,H?,&hy8y8y8y8y8y8 y8&@DpDDDDDD&PHPXPpPPPP&\ \0\H\p\\\&KhKhKh KhHKhXKhKh&tttt t0tXt&p,?U]d U/(Px(8Pupuuuuu(H pxGGHGPG`GpG{{ {({8{H{h % ,<P`p 0@P`p 0@P`p 0@P`p4DTdt$4DTdt$4DTdt.text.rel.BTF.ext.relsocketreadsread_subprog.llvm_addrsig.strtab.symtab.BTF@U@ @ P#  4$ @ 2oLHXxgolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_read-el.elf000066400000000000000000000166301520243672000251410ustar00rootroot00000000000000ELF@@ k*q-qVV"3DU{e  i!y!q!a!g<<w<LUe  i!y!q!a!g:>w>OUe  i!y!q!a!g7>w>RUwe  i!y!q!a!g9?w?UU\e  i!y!q!a!g6?w?XUAe  i!y!q!a!g&0w0[U&VVe  i!y!q!a!g""w"^U "3DaUdUg   z|~~ |$ & ' ) * @  $)-1@=K W intread_subprog.text/ebpf/btf/testdata/relocs_read.c__attribute__((noinline)) int read_subprog() { struct s foo = {sbachar0:1 if (core_access(foo.a) == 0)0:0 if (core_access(foo.b) == 1) *p++ = 0xff; // a, b, dbitsxcdefgu8unsigned charu16unsigned shortunsigned intZEROONEu64unsigned long if (BPF_CORE_READ_BITFIELD(&bar, a) != (1 << 4) - 1)0:2 if (BPF_CORE_READ_BITFIELD(&bar, b) != (1 << 2) - 1)0:4 if (BPF_CORE_READ_BITFIELD(&bar, d) != (1 << 2) - 1)0:3 if (BPF_CORE_READ_BITFIELD(&bar, c) != 0)0:5 if (BPF_CORE_READ_BITFIELD(&bar, e) != 0)0:6 if (BPF_CORE_READ_BITFIELD(&bar, f) != 0x5656)0:7 if (BPF_CORE_READ_BITFIELD(&bar, g) != 0x15443322)nonexistnon_exist0 if (bpf_core_type_exists(struct nonexist) != 0) if (bpf_core_field_exists(((struct nonexist *)0)->non_exist) != 0)nonexist_enumNON_EXIST}readssocket int ret = read_subprog(); $$];9h (0PXp?,?,?,?,?, ?,H?&,hy8y8y8y8y8y8 y&8@DpDDDDD&DPHPXPpPPP&P\ \0\H\p\\&\KhKhKh KhHKhXKhK&htttt t0tX&tp,?U]d U/(Px(8Pupuuuuu(H pxGGHGPG`GpG{{ {({8{H{h % ,<P`p 0@P`p 0@P`p 0@P`p4DTdt$4DTdt$4DTdt.text.rel.BTF.ext.relsocketreadsread_subprog.llvm_addrsig.strtab.symtab.BTF@U@ @ P# 4 $ @ 2LoHXxgolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_read.c000066400000000000000000000046551520243672000242230ustar00rootroot00000000000000#include "../../testdata/common.h" #include "bpf_core_read.h" // Struct with the members declared in the wrong order. Accesses need // a successful CO-RE relocation against the type in relocs_read_tgt.c // for the test below to pass. struct s { char b; char a; }; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long u64; // Struct with bitfields. struct bits { int x; u8 a : 4, b : 2; u16 c : 1; unsigned int d : 2; enum { ZERO = 0, ONE = 1 } e : 1; u64 f : 16, g : 30; }; struct nonexist { int non_exist; }; enum nonexist_enum { NON_EXIST = 1 }; // Perform a read from a subprog to ensure CO-RE relocations // occurring there are tracked and executed in the final linked program. __attribute__((noinline)) int read_subprog() { struct s foo = { .a = 0, .b = 1, }; if (core_access(foo.a) == 0) return __LINE__; if (core_access(foo.b) == 1) return __LINE__; struct bits bar; char *p = (char *)&bar; /* Target: * [4] STRUCT 'bits' size=8 vlen=7 * 'b' type_id=5 bits_offset=0 bitfield_size=2 * 'a' type_id=5 bits_offset=2 bitfield_size=4 * 'd' type_id=7 bits_offset=6 bitfield_size=2 * 'c' type_id=9 bits_offset=8 bitfield_size=1 * 'e' type_id=11 bits_offset=9 bitfield_size=1 * 'f' type_id=9 bits_offset=16 * 'g' type_id=12 bits_offset=32 bitfield_size=30 */ *p++ = 0xff; // a, b, d *p++ = 0x00; // c, e *p++ = 0x56; // f *p++ = 0x56; // f #ifdef __BIG_ENDIAN__ *p++ = 0x55; // g *p++ = 0x44; // g *p++ = 0x33; // g *p++ = 0x22; // g #else *p++ = 0x22; // g *p++ = 0x33; // g *p++ = 0x44; // g *p++ = 0x55; // g #endif if (BPF_CORE_READ_BITFIELD(&bar, a) != (1 << 4) - 1) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, b) != (1 << 2) - 1) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, d) != (1 << 2) - 1) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, c) != 0) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, e) != 0) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, f) != 0x5656) return __LINE__; if (BPF_CORE_READ_BITFIELD(&bar, g) != 0x15443322) return __LINE__; if (bpf_core_type_exists(struct nonexist) != 0) return __LINE__; if (bpf_core_field_exists(((struct nonexist *)0)->non_exist) != 0) return __LINE__; if (bpf_core_enum_value_exists(enum nonexist_enum, NON_EXIST) != 0) return __LINE__; return 0; } __section("socket") int reads() { int ret = read_subprog(); if (ret) return ret; return 0; } golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_read_tgt-eb.elf000066400000000000000000000023601520243672000260000ustar00rootroot00000000000000ELF@@llz     " $'5 < I M\ae iusabcharunused_sbitsdcefgu8unsigned charmy_u32unsigned intu16unsigned shortZEROONEu32unused_bits.bssp|.textunused_bits.bssunused_s.llvm_addrsig.strtab.symtab.rel.BTF/H@@C@? @ !oL7@Hgolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_read_tgt-el.elf000066400000000000000000000023601520243672000260120ustar00rootroot00000000000000ELF@@llz     " $'5 < I M\ae iusabcharunused_sbitsdcefgu8unsigned charmy_u32unsigned intu16unsigned shortZEROONEu32unused_bits.bssp|.textunused_bits.bssunused_s.llvm_addrsig.strtab.symtab.rel.BTF/H@@C@? @ !Lo7@Hgolang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/relocs_read_tgt.c000066400000000000000000000012721520243672000250710ustar00rootroot00000000000000/* This file exists to emit ELFs with specific BTF types to use as target BTF in tests. It can be made redundant when btf.Spec can be handcrafted and passed as a CO-RE target in the future. */ struct s { char a; char b; }; struct s *unused_s __attribute__((unused)); typedef unsigned int my_u32; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long u64; struct bits { /*int x;*/ u8 b : 2, a : 4; /* a was before b */ my_u32 d : 2; /* was 'unsigned int' */ u16 c : 1; /* was before d */ enum { ZERO = 0, ONE = 1 } e : 1; u16 f; /* was: u64 f:16 */ u32 g : 30; /* was: u64 g:30 */ }; struct bits *unused_bits __attribute__((unused)); golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/tags-eb.elf000066400000000000000000000061401520243672000235760ustar00rootroot00000000000000ELF @@ aq&qg88g 8 8pq&qg88g 8 8      W  _ a  c  os a_a ""a$ $charxyintnormalDecl1bce.text/ebpf/btf/testdata/tags.c return fwdDecl(x, y);fwdDecldanormalDecl2ctxprogsyscall__section("syscall") int prog(char *ctx) { return normalDecl1(ctx[0], ctx[1]) + normalDecl2(ctx[2], ctx[3]);sfoobars1uu1tdt1.bss ,,0 x &@p &@p&@ &@x &&&(&8&@p H&;P&3h&;x&@ &%&r)]1oil  8 x  ,4DXhx 0@.rel.text.rel.BTF.ext.bss.relsyscallfwdDeclprog.llvm_addrsig.strtab.symtab.rel.BTFnormalDecl2u1t1s1normalDecl1D~@  @ !` @0 XT @P0 P @ 6oLL golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/tags-el.elf000066400000000000000000000061401520243672000236100ustar00rootroot00000000000000ELF @@ qbqag88g88qbqag88g88p      W  _ a   c  os a_a ""a$ $charxyintnormalDecl1bce.text/ebpf/btf/testdata/tags.c return fwdDecl(x, y);fwdDecldanormalDecl2ctxprogsyscall__section("syscall") int prog(char *ctx) { return normalDecl1(ctx[0], ctx[1]) + normalDecl2(ctx[2], ctx[3]);sfoobars1uu1tdt1.bss ,,0 x &@ p&@p&@ &@x &&&(&8&@ pH&;P&3h&;x&@ &%&r)]1oil  8 x  ,4DXhx 0@.rel.text.rel.BTF.ext.bss.relsyscallfwdDeclprog.llvm_addrsig.strtab.symtab.rel.BTFnormalDecl2u1t1s1normalDecl1D~@  @ !` @0 XT @P0 P @  6LoL golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/tags.c000066400000000000000000000013731520243672000226710ustar00rootroot00000000000000#include "../../testdata/common.h" #define tagA __attribute__((btf_decl_tag("a"))) #define tagB __attribute__((btf_decl_tag("b"))) #define tagC __attribute__((btf_decl_tag("c"))) #define tagD __attribute__((btf_decl_tag("d"))) #define tagE __attribute__((btf_decl_tag("e"))) struct s { char tagA foo; char tagB bar; } tagC; union u { char tagA foo; char tagB bar; } tagC; typedef tagB char td; struct s tagD s1; union u tagE u1; td tagA t1; int tagA tagB fwdDecl(char tagC x, char tagD y); int tagE normalDecl1(char tagB x, char tagC y) { return fwdDecl(x, y); } int tagE normalDecl2(char tagB x, char tagC y) { return fwdDecl(x, y); } __section("syscall") int prog(char *ctx) { return normalDecl1(ctx[0], ctx[1]) + normalDecl2(ctx[2], ctx[3]); } golang-github-cilium-ebpf-0.21.0+ds1/btf/testdata/vmlinux.btf.gz000066400000000000000000053610701520243672000244150ustar00rootroot00000000000000|T}wI6!ww\@TB;4H;(/|=gvf_^y3ܹ3g\{+W)-[9ayPfg!ŸD<ɻ{A#s\q21cOvo7^) ^U?E;ʽG o?`ҽa"G-La ψ˽=k ş±gp.sDDʕ(hW& NHT7ߐ?1@G$Sֲ EࢁNǀۊUβ}+h2%np[P?D&mWB\SSX*#Q#SF~'Ϡsh, tn$iKJU1V*x5 V5q\,?vIi9!J¡f±cKt̕N`;)X5,{HO"^,y^ت<,W8|Xd"P(Uaj=O~@qp;u-+x&zkY$*.r>`"5cM)` P.%_ [*w#;)JGE5܂ ^nE~kbY,p 0x.= ~Gtsi@ A'p(8'#T~ 8%Wg/s<*%$..L1:ԘnK!HJ U]1*_]2o&?w<*ݣ!e- NFgT߁'.Ԇ*x&)H dقR˂Wj$ ^K[SXH<(T xȀ <8%Bֈ%Xo x=#E> <]܌B'+ 8Rܱ~/bScK܁g ǀ׫g>Գ.E0%ccK[x&9G 6QgpdeYHp- " 3O'Q':sԳq8Ā7l{q>>I࿔<A0K**.(p|sirXb #s-= *'V4 aA+9Gsϰ?}YC&@ey x}S0wq}RaW~gׁ|W}~|.?JqjI\eqO ]̉i4g7فuQu<vx#e#. 8 6t?GC>qp%,]~kqf xv޾& *̴xJHUtsvt/e~e\9>9¤;էƯI|K}l}S}d Vp'=ÍAX{oy]; I:ia?>~]Ejge~_mCWiep^0]nO4QYaL^5aLaL&I%(ef%DF}ݮVxL쵸I,Z, 7kdIAcIIxl)F!$,K.p$(pUp$2v2GyUzN"cB3R Q_`1YIx#X\,F$t!S:$zJH$2$ '~L΍NASm)Q& DʳrIw4nBA#uEhܧarEZ}JT<< Ā;K%-O{"nŝ T6#> ))IpoϪǟ=o- њ{L6A? zĭ]:%Ofǵ֣,-?o-eX[q{2;[&d܏Au3)6P|/&ؼD,Rl5K)ݓ%g:!<}9]XCVh K{`F\6Ka)N2Ha^.)]X ;[Sx5 72ݓ+:^BS)9iRrsY2%bJ)9f\ީ+\ #ex ]Rzmpyo~_ Sئ {~}E%0iGI)q/_a-%Q\lR?c^?%;)/>y!2Q^8bb7caMJPSSVppTR.ǑSS#V =x6 giZniy0,Y\N+ޚᷖՋP,P[`Bh|>I8eR(]=*y8%0d^ G夾iuhltnȿpI~Z{i}Q{!RT% b H[osT\o/@m$<|4N֞лKkuW a4]ˈNcSTptU uS? "n+hmppt -zv0=#8oǽbO}ܓJ"C}'ؚ$㺄q҉<8q:&vp?QzWT֓R1o E8y4u!؝X,nvO8cEM簼XӱL7?as0Ls4W|E:jv:)s!<9P\M/R;H}T\?^H$Z_FR H/ ASϏvC5y6 2.;i]پtTB5^=|^MtH}2n!OWyD`&.I(C_c,ixz^ >^\J;u0A'4OÅ K :,Ny8oI&ؗ1ܬ.2FR ǵo3x!wE z] l7E>;er7< KB6t٤L68?R:ޔ3;>g ™|Lx.u\Wʘ$OY8}vm[pz9LQƻC3r48nU}E:%n2¤i^cSfiy}:ȨV].4"NG2MQ_F.EFg@'|v>;? ?¤ ~bOX 4dgm+fmקl)]p>˨՝*OF)I$H{}Oo>Kj'}J>{&f. *&cמ} kLҿ3n51;2C~t9ې(H~IѸLza:84 \Q5JFRܲLcO }t&g/Сm;22I3&{\Fs3{t=~ .'\E؅UU8jF(Q5qf4uz˒Y-g2sTmf˹k:-@f"Cmj/;M3X1f-!3 t*OcR{mطVc/tTx=3:y0glf.!33˺_< /=\ϧ 2`Zx]2Tfcw]]<_ E;NK4p_兄Yt^H]<&X37$2e]&ˢ]]~cO%w QqJ,:Vou#Okϴ~7ط?E9%:R4w蹫nݿg{6ZJY>ZslR?dᵗO{2GI'4Ikk/7e_͜ ɷOt:vH^x Y Wa#ZsJyqJ؏t7η >Sq+p Y~4'AO/[9uߜz}NG$$^k<ʡێ.I{>k,CxgΥE\:_ʥr.w5skK O\;cd.pZO@N /^)|pN1x]X ;"M.Ksjܶ:_z8jpqc:ԓ%BzTfr=Gn=wQO=iuV>h<͘ |#|yg^TyGE'tU:*'u #KJiOXp^MyK:k"ms3OeX'8u{)+kp NK8- Lii99jχƼ^޼1y8Džz~N^=.,/e{6 iqާˑOP">f72_>hEvv瓰 g)ͧgdr淬gl7&v=ܴ6$.[R:A8Q~ާmx~?8 #[ǍcA/mp)aZG+}T\O=&ާOSW/ipD|O=*ާnXV;%Ooi0%OU|BU/})(lU~>W7 N{ඤRq#$79=Lj,/  ;u~YHg2/^wח|YlSeL=8r#,-c 蹡)B!|;aʧtti^#Le7k9(Ll ڡ}uTYjLNѥNΛHuƻg-ؿ)`!P nRgTnTV$礸Rk9"Td*("2;*'q_W F*~97({ڣ3=Bك7("O|wDB|AI [^\d:oA?!yIK,O0Ytp^k^r@!=byTfB2GqsF"CuİBz|\!}~B^>'Zz0uUzۖRzMBzlp!9|]6^xqga9H >w:@ģIDž%,,k,=Fh-O:GƱhoy6a}_K>t¼^(Ga/ Fvn] ]V.le 3 4.lI} ~º\VX.+ ~N%*,ci4}C= ~qTE8G"zd=TGC<_~FgF~*y":*I%H<.*h$)*agucBnF<6/jW.gz/O .y!w _HfyQ釂9XxL(mӹ!E-*}+vY[>6؃vK/8T>"bC4wa4$O.&qNy px2' GP1;.N_S˒dܠ07+goP>jWL:NmO's{zw.'yC]^lSh䨤i>nYEYg{)}”NvWơb7|1q7d2m!72?{Yv.'M +ewԏQ,co|L"v5N7.}Sji3ZS^ T\CpҧGd ..PfQk(5cNU?x_?=<ZQ\QBMR\}ė]+u.oHZ+LiA)-~|WUILʌ7ʞtYB%t.w^B‹p80孅i\a*' GJ<塿$YmF%|oz퓹Oi ih}Z VܹlEe䃫bV'%nI=OY@KS{yKuUjW}?v$7.sv!~TC-^QRiCIǔ끒Z*%uy.ZH:W-vG_}oIWF=txYq~`)nڅʏ*mJHP9ŕ_F?H5G݃-~@cWQSSi 5r6Kh~rքܒDz˦z_}R:v+9쮞5Fc)3#_e&TJ6$LuRRK\P-ϔݢNH~"EiwCt|QHRo}%(\"-<\ p]pN#ͼ\z-_Һb ͘ܮoK[GCg*(ZCNLuCܙ+~ ?*-kZx!?{!#\FxgE<02"NUFo)#vp2: *#Wp0SP~pAOU #Nkp\S[w+#?6Ķ26 vzN=nt(2z 9pS8YFo ;E|-ǎM^{ <2{.٬z"W!YY`E"ܤwȔL 8:M[Ez.9+ DF^FJ!<,{IݩduqopQ}j{LZ;;JsK9H TXa|R$%K/y+HϨI TazWSGeN^,rw}t2G{lN}pS^'lgZ|#,{+@Wxx'?+pY  pڦ?JRHg$T|p]'>Z76]t9]ҩuHO=%+*-`đZ {W Ic*ƈ}iyIiӨ{A YƆBal[]Y-wtRy2uaym?$7d2Skfeޫ#g%Q(2wUt kd"srx'}"iɩ P)Ud ܴ OlWEϫwqrޡc;S*KygU`qU\ͳ}QF31qlݶWst}_EaΪݶWނ*z*d*gm*TU]ܪC~r,YUVUq_EqQB=o Qh_7*;KUk2.]v?WWz/ɃUyHgyI'!(Ci'UU߽aP>9Do

a..PMR%nqydN1d?}+A= A= V~/SB^Ƀw*yn%>QОS+J|WɃ }ҙ)UFLlYK}'܇d9K.GnS #8}.}l5:_{Sw"ouqv4V5G]J-o N=:! yd \~9?V]z)!s5l<'p\xRR| ziwΤ:'Oz׏P?Tkw=EǓfQS횺~QS׫kY4q37e4OY~Zܧ"wkՒxp5<Ӧ/ˮ{ֲbyZNqx5{w;H55c{'v'gܟiW0晧1]uΗ 6BHfm03_h $_\Kײ/"!Li Z'/7Zz^-=GB(Z2G^-u5vyViдn).|lP{Fܤ.b3Ƴم7mj\'xe ݁Ƶ=|GQ[k`m}. }tD rOUR-E>Q[ߞں>\ߡ!x3|7v[;ۅ/%|G^7q|ϨNgʟ; wvjy eO=uL:0Bu>^[ZbG嬩7KFkVGGg:7xm}QGϐQ 3NMotH:ۂ,]Z~Vෳ^DZs [^ Ўb~#L> ώ"dM؇|P~} :HbH7T=IL> |´6>x! G4v]E; ?/rF?}eg-_; :1){Xԇ1,+<*bZ1<.;D'Z^A?z\a^]Oq̈F 752nyd4뤸]]WOyv_]=qwSdh;˻;{gXޞ_wX7ꛩaRU>wΊ;o-g=Os/r1)XUzm]NbyR|ppyytR |ͩE)Cij6wr |ɞVNw==^3w>MR1Iil|=|H=P ]QOߦz»%=trtqxBpL4*;d'*8]/k|˸<8a}KeZz?ovag\^ʷ:¤ۖTuX˞RnǀA(x:q1>U4^6sk6 Sk뱰4vYCw$הsP>/D/wp880eMj#+} @U{(șL7p0;סeTlfFHO+yaLR _$]mWa;'SNNW(nCd8V By&8dwr)WCN ƯBC=v_gh{V)\mI]2N&sWe8POPnLjg6P45pkMC=λ4..g4亐ʙ3t./5}:H4$ㆲﻆR&Y'o ޲5tl$y\ j”q yo{%CMzWjF쐩}?hF1H{4=GR#ߤ34pHc^LIKNXXYB´JS9eo,D߬Ll, C˺o1>ZmzdUcn:侘4::yOQ]sO~ܿfiܷm C0 ȩYZ=jcT8XXjzGc=>-@qN) q+`ecݦ|G,sK)B+e<a;s<7x&xFGw }0(xmܷ܏&W;7m&z8m=PtLXW 9 zf&v^rS{b{_:)QF8Lz_dFGzU:%1L&Cc~THk 0V/R0O^؞+'p#?$X<>mg5?eŶ k1]tI3iӷ>Lk@g0};7x>{O }7(NpF6yi6!hӜl&+ɖg%qL$[>.d3lx|Ƒ-2YF|<>Ȗgl"[>>Mdl|-oci9(R#u9R[ mdi'nO5"y)_kK#囊Ցoh;@SWgl]a`:X/G6p~ݍ(+p.[5)|b .ІV"=nJЪ 'n3's0Fil>s%ۛJTvmgvyp7ב6zp~<!/Pj(s\kp򎳭G^H@[}^ɶBep` u "y[k5~*yνy+Ֆg]PQ P?<}+|OzoQRA&(~䃶WNcߵ|>eh;q^rpAɘ08w;u5Rܡoje,v{Å{mAiuZ;Gg;òvv2,7P?֎vo8 %~ƹ$bt4|kϼ0YAhN,fv1Kgl헦-c1o=ص=M777sg{_^Ƙhj=d܃N}^Σ~-ځt%xmMJAw`aA|+ށfBX兩 ;|1^;n`.j|A'LA 9.fvr K=<.˷*6t4 ev|;/OScGEf.u/j kđ?ҥɢ+#uyWz/Şc8vgPŅI)(yQTvtWtPE-u1ޛ/IIr7Kx vJubx Igz,z,a4:˷v:o\w G_g]YTvAE3z+uuj*tcHm1^]Ҩ.1$kZ=&4[Ks:Kt_] .M],u1]%RON]f\B"ۆ_wwOU?GW]8V:5/tѾ.WzK*L|%2UfJ[^/ylؓb~o|7<|q}Q9n8o銛ɽTjOo)cxz|ا_ ]t1vrvQVKs1Ч<(n&~g HgK(>*y6䍦?%ot/$o|瀯IX FV^. rdPq6dnjн~ҜZǮn4?<3Cg3wc)=^M[ ^;3^ta9FON+\8#xp&> WM0 #~Ǎ'q{JZ,kfKEX)32 whwc?AF7Y >;4ap1YCu,owrVY*Y&_J!85{pGC9b誟z7 )3u9 p$_|3y}?u߿bcCB7q>>*.UY I`PS9Z]-kw}T6ߤ25|]ƮC |e,aXGEpƞ2Lv;ﭤy+o<.ikwLފ;MH1 4qҭGI 4OKό\=i7ո3K=zp =ՃvadGq2d;6,YI=9d{rRئKS7o j'22=;rl4zVO>w"oli=lS'{~% % ;S&zoFZt1=kD{;ka%.V.s]]\uq=s xht+)?.nT[##6rq>n |ӹ/&!}z1}XS~$1Ic"CifY_iP]W񶷯Ѿg(\*W8y_르'~''obEkzŻ3=m.Z+D\RY&]uuT}^90=I n{:>C5I9oegdO~2oMx5*CF!^kk3**D^{}3ᗎt4%dGrSTqbpzYpqT\!o;c]]zCwn=TD7Ɠۈ\)҆\pMϸWe#.}TvKM~}j6ڕ5>gUI1=nMfU6d}/i_=|[ȘNM0Dil|˛~>e+t55V[_ VlesMb*7xeTߨ詾9lA\g/aD 3ԙ"ƞcgKO⧍ΧT0i'uS%ב'g#H'MxjWr^ƻ:qO8x?k } y˿9Ϗ9ȳz>}}ϓϔѦ0?eUm9GϩWx蹨=mzs7?~&}(Fnq '(2鬏}(S2 td!O_*je4;g~3iL_ N}i4򝟼7?{r|n~6F'?:pnQ[:932@Ee/>em7>=[(ﰚ=^m8ʽ; ԿϤsq 2㾲4O|y*?3[8ZoԑTaěyF?Pm7m톮~5S;0z|+ ~?#]C;qw+sUgV[N}1LkYxL༛9@5x? yt+3ԙ&ts+3]8cS ߺ/B\vY71Ҩد';|;@i`[C;PhoOx5ʭ|K%nnة+K&hFv%'k9^G܎gsցa9 _Wn-|%q 1ݿHb ;t%l f-ζ`y+Fr兲#LД9ɣ8Jo'\|G"F0iH>7(wk9EGC'dgz}ܸD:vk)n$ݢ.꼾#8g ׈ឋwMoHEH3G +<)rM%>iYɃo(y#%~('$L_⬌WSJI7P7NeW=+JҡtI{1{U^##Jϧyߖ0}Ygr|dXuc +~Ge4 {e=!?S<8(c ~F2HH\ B5pGi=פ6]Dۏ~n$+*M~+E}[Jx лoI> `w$^E5l$c!"=X9A昮Ci,+ rv+    C3CcW|_eYvt~ca:X}~y/婿re}|rtecj^Gw)uccyչy>iƓ:^(ͧ)N8 ?)g"C-z0[>+_J |_+aJ'$:J:Z@aQny*--x.;qu+~!vn”{ZN??7o}l L8^'NE㟳c Iq(=8il8%L}T^%9 ߥUơ6>ix\t8yJoY0^fe~Fw; 6QF!uX,Cˡ |8)E~tDz\h=mv_Ku6ѣNCkؙ e!2OCb/47pJHxlI4©w0tsO[!{cm?)lOS;l9IOr@tyCڢv),0M4v6>:o30mI'~O6lpFȜ,c_{/LBT'{qRFJOs+'|nmy{F]C=/N^ 71at^2AU3Ncprvr}eŲbٚ_lX |cm_齘 ᱛf~ǍK)z3ȄJzQ@J4y"+D.O菜פƼfq!FdOb,X$o&7ߓj~ =H k idv2j!z   \8ߺ*,ꏾz[A}Rܚ$}Y^d`&=Vr&qYB~x3tQiӦt"4E凚k1ʒLNkvdq>L_H3dcfW1dN['ikdN[[&sdYC:&y2> g$g…p)z:l->We)n b1엧9.cXVV|z٩|a aoZk"̂0,st>yNtnڲa }mn5Y_9Qs`և f`6ykF fSrf3Utf #a$~5̿-60S|kYT}.j-_}5fG{`v-籠\f_l;ϼ;_g9u3;ˢ72 ̾Xca60<s ;4N`Ҳ0aW0ig)~a9~b΋U/VX`~ sL4߬?M ,L^kSŪ#a0RzToױQjI? y8f.`ւI}0R[`.yr`Ϭ sV?`!\ O(]\O2.#j07 sax6[` s+Km40w,s'̚sx>.׿cn̈́yway>W90oě秓r:paJ_4j#UOse=\=vXSdډ{Cr0m. X9WS{< b?B"gWi 6ڂreJ9=x:`Nsy΋h;-K2=z>W({YS+k7M^UouES@nh'te`vBn&?jnbZ_=Vd(ۣRLKܹ`<{ ~z6`58}/9y();* # <vgR; ő6`O+(e|<=ЏJa9 Ӻ%´nӺbOvӺ•[`Z M\\|Cd56_k vXuaQ8p}p  7nBg 7n^,IxpKAV­ZHeoCznK:nv_8pG#_( ~-yp №W]a'+R~ZѺv"CP{/+LΉMs0ѷ(7PFt,]a0ʴDž0|_0 gF+:? 0iQ':_E3X|>2eQ25Q8=/LyFx6[Z»~>r.Jك edס9ؼ6F}/^o]4pmnr|6.hg]'[}hK-/=fk)_&TS2”-oRWᥴKdPZva}`(Ŏ7xzxIϿ3pV3_w~Y L'i޳||cUZ*}H9)sB8}kpu^cX,X%FE܋8hmbB}ܤ1:GEoZyVev65gW65^dzA"oG"=AguvԖX_Hiu9CJ 0ƧzMi_.9rka*<FΜTk4g>)bssn ޵H֎ a*ʳ^1/|+LōNo7ϋ'Wss9.L}p 7(u Sb}yuܼy=CYp7gX=5?Pkb-oQ9<܌rOr:n^f1(Cx_P[79 n{'n*|oKZɘyV?b7~%hq_ɗu-u-um%2gF6A*K?Q[,k5-sPcD\S,a_{UKx\̵x>z_$$Ku9A&Y*ߕ]sj&6Z >&iW)Z/{ts';;-s6ZkҮM7a?C_dwݩԯ2t;N2=/?~| ~WU†?ݍ2eC%i)J!]«sv̥e]>nNWɥ7xxDuʯ#K9K,^2ك]RDC9i3LpJ˴= ŕ ^iv;}Og4s,z(5̳.^eNlsG9S uї6s'gc4۩u>ySH~ . e{Wxg=*Vi Q^<5y+e+:m+$ ]s?0hض3~c >ۇ+tݵR3!J>ޔ+J.CNN&+jFN6xO{Tku.SnE/;Ji`Ͷ+~(UQ>vAsy[|/ySCySC ?dfJI:ۇnlJPlwlwl)߾ϕ6< <*V ]3|z*h}gF+gOW.3sĎ[Jnh㾯{t|#ڢ[?ˌOi^o ?!fOɭx=mo/j9Ѝ߸ҾgLhd>aZ[{l%Q6ϩߡ3g/&S8.~/{kK;?^VK0KqZi[o vkSS)a;uqMZ ^ܩNwwrbH#m bO~ss;&p8]Q۹rwh{q#-RZ!tvGŗJ n:Ot~:4d2FRVq*f:%i9#S p=9F {VLWOOgIfÿ>/ateˌ2Ȩ8b(M<5; 9(şfjnΞZQmqߌ%ܺe6鬵!a* >fWi'T upe{L _.\|x/?#{ƈ=܏acV~́=܏{c~L^Ǥ+{t/c~̤܏1r?^<}XK_wˡs_W;߹wŽ߹U}p_WmUi??q_>v}Ց:sU}Wq_5~f}Ւp?U}ձ:{?U7W}~9 {޳>x|ϼVDUaY<=o%;67̜0+260z IY-5a>p=dYaf:ߛ.ևxOưC'C'n!w \:{=qyO\<^0t 9x={=q#=qxO\c'17;{=qCǸz)|yO\'q78r7-:.yOܵ'.K}:{⪟=qmO𞸡'xO'xO'n w ={2=qO'yO\'.$um:{Μ=qOr{4Kw;{j=qNxS'n);~?{yO\Ӽ'.iW4ky9{>{▝=qN yOܝӼ'i |E=q gxO3'n ;~F0QY &aZ?yUG( q?yw(*`>G<]Yԉ0l'nz%=Йif0 L~) 9 #Y(n+pRcϏ#:. /x+ 9bŌ>#05r G~_^w|{[W<| LҀ9^G9^^^ˤOBqqa rW'A0aZQQTֻWQ6޽EGt>YGc=.ztqTwYupVnCi,fNC.IP_ܧLV7S}u؝y4go c%7I[%Xtz;K\nǽG.oE=/lwˈ;eٝRn{{ \1:H4\-S\Cߔ%g[א?k%ggWz2_76-y|yCYɷ0وF:VZmH?iصI7}!3abuϻfø{lk5p5ݯM+ Gwn3<b.ǯK 2=0+h͘>8\ń~]-벾u.|$/>[*7=is= |Yss֘]Ge=~)LM L߲I)|G\Q4n3 _ S}+Lk]$Lk۶ 76w! w'ސdl MÏ&> m H:į?,^6I8MQI?Zs|]$7M2;7&J$} d|" 潘JAA}ҾȾ(dl| |M(cp ²nCCnKhp|<"ypcg)߸Kk,|{SpTzpT <yQx?b];;6ˈnF+Ğ&=jp?u*zE?#<#n)9UǘߦC>Sͭys3ꚑ~c8ˆݾUeGՉJx'nI7ϴ]2eoo(=ߴjyMbf䱻E7xܔ=F7yś<.y&q/or릴~}X*pp=p-v_}- q~K}QIu[rKѿ>Lr5 -uχuYܧSKWqz[}Kbf6ڝ[z^LWEMŰ[wwxO҃V*,?t{U i{325c!{9nJT-im6ϸnsZzJk.ROnJ.RZw8vs3ZoZ_~7R;Vq܅40]0 tì36Ls]>s]{kw}nyKwںyxu.&&=M{d={iX7p{دya<C&_> 2/O2v< v?K\RfHn( Jsĝyqi`2%릫W1wxjLys:)yqG5O_2"\ eQW3?uOjՎS{ :261!bO}F,d 5$BP\`C. >C. =C. =C\˂r> 3٬[T]D_]؊u(}ry̿:/e?]+$O`Yz-?Ӄy~.=o|e-8#}ZIf_MB~k!i37g`gxo-3M·>4"7 i\nl5_i?Qc+Ԗ ̱#L+l6ڕ=w1}uDh5 eia=m*<}K\{9CCFN>gz]x̻HPxw9{Kis~s\”'g:m2i}e:#ʠg[8L[S]=hS!EØ]A*>$\ g\ǜ/CcWZC ?=YCte?oVJb8Nޝ 6׌R}󨴎_A]+珑Էb)i/8gjfP]y2Ǫ>tEs?HGgy,`1t΀K2r1'ǶOu0o֠d #\b%K>~HgωL-*la'AQRֶO磡1qX' vݷQ٫z=?]i=|_?x]TZ΢;uQ8ƨ/)l]|9+2 AkOk(6E}7,8Kpsrs~i N水gtz"6og2iFXnl]l'z'ڙf9?SX!'a':n|=sF}A-߆v=GzO]Z{Tgϳ`6zCK?܉ϳ!Yg4ÞrYA}sN kv:f/$}::e/op #Ky#ŽOuO82?eWwY%_}FC%_~ʋ5|tE5ѼQ7?u5ӑ[ˆu o7H!bgm3qτ<c-g^\GFORz Q87Tp;rl/ <.~.)93r)wؔOˇu[Kɤ3¦&߰8|LW <6}=.dC!jϚmpһhɭˡOY݁ OW>:ϒhlʞ,CUcjZӦ¸ 9%";qmBxϴ=[/-2`Kޫeqy RGQW+LaLCơ=Xp)~hLiӞ;ODZ'^'LYzqSrߡEª-oR n߄Ugogp M{˰x7ݼmй1ZWHgYq(:L#u?2szx+s~sO{ eU"5 .a o: }#ɔ6d?H'LR?t\KDrJ*}p~\.#ofhIoR?!7^J´k?^HX~;\Q/>' oiRi=C ^OC' K[+oeholM(qVSrUԕJL~`wT$[OL]7deHԹ--O]}c@2 tt^>{ =UكH<"a ajF.gI" v8 oc^H ΆF@H٦a>ϵкȷ݌z̖)ow:{,"~nhiL~~ⲥFPaRqc6Iw;-zy Nhn(0.a}#~~Ç/V ;%`Y%KvgKvg&H]e/#|`楽^?Qev~]LZwpPկAfv%}4<-wk$]:I$ 6{r;l48 E7”I^!u9o҆*Li;ck@-mH~7ק,CJ{k0ړG>B4鰎m##(ZlK@Fۭ~mrg_ͽ,T ky_LEɃT6)Fٴ(yq_aYk.>w7Y؝~oQ/87]/}唳*u{V-;~v.Nҟy?}S߻?#ga}&P>_+ OG%g縛{m^Z|>=Qs\W3H}^Σ:sU[iysY>j#sзdM$v'q|xdSJdx(EFz3Ĩ#oy sUn7.Ƽ]+F(?fE܋Y\37.obGt {S.txͩNWk?a-Da,;vǪ`;-L CtbMavvc[8#n7ވ-O]cEiJ%=vXO.۽ԆuGJGԞu¿n=Hq9<.g#g_y*u`vZP#@˚*(713fjs{ַXvx'xG>q_ O^o%,iaL{),6z3|hSVȽ3ෟy>OJj pr^Y?og' lI[S !L'\k]MƬU%uzImY}e^V/N&#~ h7^il> tx~4WMi2|;:~=Fb88V 5a?ee:ƩtԿW册:rlO>V¯0Օ1 D!A@oC( S"LC)|#|P"7>r\O}2 *SZ_5S>B´7058,Ll'ߪ Ns?'B9-e2vfas\0ةŬ7PIa_G:02/kksBw9 n0ily!c2x{b麙Y-۠X;.o9+yT™T5Y%PꏤF mFXO[%^Rx57yr~Q~2+ik(9ӯE.ЏTQdʹ[x}~-/lЃFkFg u?~SO r647|Q~S#L݌ά ÿay;`As&f6`fU/`7wl ?϶!U3nu~,޸܏[Mcsrp?@~ }G߱Tm7y? rg<(6My149 9/OyNr΋`<3f6[aڎS0:aҔcp m0invjC`Ҷ0:+aҷu,2L& &3t +ä2L `ҷuä¤o`ހf:`f!`6 q0i[m50:aҷuäos,0:aҺ0:#`ҷuIYSm 0:Oaҷuwso딅Ii3&}[g&LVm30:aFLa[ 0C X`~I8ʗ0t2D.Sg4)3r;#)3r)#)2r2?#)2rr9#)o3r<)y3qR5)m3q28)33q-)g2q<)I2sR(3)3s=3)2s˔L9˔'LI˔YL˔Yx YX7nn6faºu6 &nVMƬYY7 nzeeLʺ5+fkV鬬{YY7X7鳱n eh^x,XLBٸIkIfne;_4yz!#ۈ|Qqg']tC>JQmb_GoJuӸ ُ{)f޻3 8<ó^px#3}*;IHogMkoSsynn]v LuSwpxհ G[VE9|A}iT\KSqڥX1Ǧs"S$ޕ?[) )'I'`{s.҉<,9sY9M@W0ΘL'Y9']-eٯaM!/$,M?c OW4g{߈;G aWtQit0 6_9}HIrJcTN-g78~㲞Ai~f~.W?Ѹ?K߷'@:9sϐɛazxɜϹFsK,qE!^svj@ANQdV̓H96JiQD>[`moN]w&i +ߤrdќ<Pt*ó_00Iw:\NOջXr#ĞK Sd SLy*Uѧg4R.Cxxqs1)xl.oia:a:(ZTɃ׃ZaZuޅߟyܦ1Bmh\rz|[GLJg .+(/(g^k#N.cŞM^qxp8x^vWbOE'( \S87+H(&h|_y 05F%Ä#sHphpy«7 ǀ o>>/|X 8mp*G/a^ .< J8G8|! R 2ǀs G ?~ .L"a77/ 1g .&\\'N$,"CRM^,<ScwE mpF/~ n.Lnpp|ld%.#WD&X3U8Lx8p".bOVZ)\_;pp[p2_ n([TE=9Q8<%j?M:~QΏWMr M1iۀ GONߊ/y(|%&$Ё nV\ (F2ƊpOY~8 D܉pXkOCp79KH. UR&%o> s [4xx6= ^UB|tw;]Qjr/-3?ukUPsXku45Wmz5ӷj521hXٗעóֽ#xǞ4G__]`C2)&ͯ]ߜ=N] s|@ |DC}|27Kr_cXO{vuFMm-bL;6Fl6Ϊpی:q~#O{|N6qn?rMk?~)jWE6x*?U aC%g}wԏԳ3'(ߖ5K9Mng(Ksc<\nSW6uSa7ihaC Z´VX)+F38ԟT-bOj ә݄Wǃ_8Ld(/vO zk佴V)?NTF3 S0U,L}&´V0%AeUw>ԟEh߹@Oi%BPӾ^5q=㢇Uy|JFԧ3 1–'0?gJ 9GM>`6P0q = vnǠmp A<Dp}Hc6}j&Lm?-=:~oFPMH|AT jUS"9Op";#Tno: ndn}hs8Ye͠ .&)m Ke3C8PVE8[Wu,.ci]r<_Oc7ڕc7zc7_3?yEx}9r:XQvEs%=\$8q,cg)}eYle[{0LgAgFT1s*:&JXp~(J;y0ͼ2m88zre=@KI~"I?Q!niغ2O+u>mE[Y*_Y ]Y%*&ˡ*:TE2"LKil0wS<3!qR|^p*\*ϾUϤ/wU{) ӻ…]9)עWע7~9.zOf5=ΜKS=fy%˸3 }4ssT~@uzu "L|\uF˄c~P OVal^W%1K Ώek :b[KPNjU[Â>Ӱ{WOɾa 1Ϫ^ '[R딃5XgOkZXMNw j:uy͛~^џpgR]=IWowluugMEu+4ziM̅z. x.@3FqFrd'sg $ XW>#߽Nhz7k3ZPzv~]wn]YX`e.R_}Ozgw.㜋Iq}hQtx؊Zi7 -  - dLYTmQT]?E ?a|U9|SU:q+ghU#7 !g~cL~s\)n4x8k%ZީzZ>zPyh^p'+![^ |zLg4ގw0^7/ nmp )ߒZL+B jiaJKN [JZhi!%C-9-R\`M&h2[LV2Fӊ[M+{?LOyf<3BR"l~]ԊV38le+NZK5\9}F٠kzؚg֜>G95ϯ[sݚ֜>W5í9}^nqkNZs3m$ϼ>+Gru$n>Gr.HN;"9}w$ב> \m8}lRkW kӆj{Ƶv3^mx6\n{ힵ<-Kۖk{v^my<-ޖڝl{vV;kҎi{*v^x={v׮O;k7юڝk{vvj{bvryq&kẝnISb< lBuݴa 7G>wTSӞ. {cvdF$tS]W|4ͅ˷kϫ~H {ݦ @6l{ L| s`dOa΄Irsk`Һ]0 a1ET81Z`ǁ^܏VӨٷ~i*-|^!?#{LB}V_tαΆ},s>"qcac'#R|BqёC~4~iy|yC6xYh?G;>"ؐ'9Z'ռ5ɯ"$`g]Fx=dhG~ᄸ:;:<ug;N%5DZty D5kSש2^ 7vpo.7}e/0xK ^a\灋tySEgv*̪P=ꐹ8 +4H< >G">q+E~IWEa'=E߁z19i]CZYԮsM/FCIL0sd]-cMJ?~20(+2cX\$e("'bdB HĞ#(i<KOJ}C <8T8o28_,58?}ߙZ8O,,k$wA[Q͇Ft~, nO.?iOWJaǕ\:*S&ʳ ´0}nL>J<L^Sj6x%0){= O1v2 hOnv} e9=y"<.u3p;>IYG0|^Fr^O/lM~RY۱{v~1aCyaϩK޲;ܯɽx^cl{LPo~&Eo97?S7߯'GY-:t!/zڇ m|6•׳'}qOt3nS[f,/Ir%cB]&LH:/#$;>qC S\|%OCHʼ~%GHYI$b$H ::m(׫Τoտ'y$6:SFLh;uV1 gtu~o}&lfM3uЕ{AWF3xfO?KC.}J/> 3?VzpxaؓlA^ۑRݢ|2Ĕ)?|tVs8pYrRTF{m2F_i=gC}ד"y_w١/1i0875A}yc 308}n+Ԗ_|gmb_eq\}zO_][GT1dK; Pu'322AulPu9Tߐ:@੧'}%?9~"2#0ObOll p}p,y@O}g1}!pOD?aCލ{)q8r~z"3ΛN'A{5b);?kFW2yQS78r~zy_X\^HZW^JwTQ*}A^ߵGc<\=i:O,,niLs<8{pX^jgal|& Jw2˥4~җop_9p'Yfݢ~Ϝ)Գ!arAQ%44O_.2A2K*vV~ثOs̅~eCFͱƜ,եSBХ6E?v?Eiʃ*߾cceZeLwyLkOaUro\=A*j$B=?gmŽy s\*"\{\M#pX^+Q<\I씿 9+ǢtqNqXt%c{! 2{/G.=:ƑX,Y^vl֛s4Yoqwc QJL}}ӕ}Mpzt.Q *sv ߋelG?$,uE6Xʁ\~q2^| aEI1P6 { H 3?1t Scs{_Wu<' K<"-bšxx!{pK\+oݹ77y>ye\8su>=)Q~CsW·ކtg#sS2hc3sQם oxfcS{ 9AQ>Yn!PΞ#@H4}Fփtg>d/ 3p\ana =g?%p$Ӽl #P怟*0U3L pBd8UOng8wO:K8p/4eo8#7IR${R oO.z֓{0ØԓΚ|;$z 𞞼)6'|0^ ^Ob/{x p^\G<w(*q^gp^$ak0!i;2<pI,'6$װL^:7/½ =p^:} {azlMl|lGz{q]7C6pfI"p!pIȿ0~o+9a0xpoj3lsO {|7c <=7_`{!B'Cz)'GzoCW6oX073ƳJOCu!l)1὾y Do2Nԗ? c*bL88T 8 _3{ݪ }}I7ò]_}H8a<ؗ@ݼN_&Y_}oz_6Ak.7X[g9c\Fo_|cwp-1~Tk^3k=ܡgzZBcUkO2{ᡀ{k32Lqԙ⨃z<!kW~9⨏8^\-m=Jy >㚀0ӏ7ܐݓS(ty e_*%hGCcKp8#:BybOʋ \6pw*IiLXQayg;>,q(WIĹv!=Gھd3+ xp(_s9b|`y31bQn ovGFrad1չc^aF12c,}cRUTP62Acgfu[XHuRv܎1K}G^ܸe?8Ax8 pvX 12\1Ϧ{&NWPV xcoe<J'iLĸn Cbq"c'= &p|6 o{5J \.3h O85/ancӖ{ f(ۦmq`m8XܿKEui6{פN5¦Uzg7v4WzE軝\l;kM `#&+ Vv'{ LC~TCϋ_!v9 }V!hۋ>x)XľP >>\]: }kXw\׍r-:? 3>1NhS+E8oTE^ݕc?KJy|zzC2h`;g;;yv<!-zSpq^{NR*8=u1@14&|]xqRW\6Ԯ*) R&-x}RSs:QsQ a6|U3Ƨ/K1D0*{[fD`76iM`~n2mXWf^D\k4M:⣗&'694A?0unah52"\AuP53ub*߻&;ݼS.D Uݙh.Tj% m9e&e կJ7amwSӯ0;`sOr2X@ݕ&ފNCM )$]lVh.Xal^oC LYômxa[5L9q9H w 2pu,k 0 _鮯m?>ζUi {7?\)إq묫ߧeRk*(z( CL qcS]i@Υo?|Vq[vЗN 86@ a0oNm682\wMU{:I.s&6H3aг*zHLL81EjƘY)z=ઊp)z==atEcGOѣRUАܙFc0<;ڨ3F#j*c kckTX>b?4Yg/&[6I6OLEWG8(cͰ)hCXd慠GAS4,c|<4V<4Mxh 4).hCSUL'A4ւfK<4=͑xh L4CBИ49&hC+hJCLԌ拠4G[gHY2Yr:5a?}=>7r7ٶO,}=>ٶ-sm }}-m ضc-m mضϿl[?-m G뵄>HR1VQW!Lo-|Gw L:ۆ}qоdCFOpaWvy:auPH:/@qpy%rSs.o9yF7kn43}wQC"|o6Y.|t\X[%d<uGVO"h}l|yz4b1uùB;L<!#cm*>,X\V^ggw{mgACgn8cm>/^D>sWOl\{1Ǎ>lSԘ۔@ć243c`g1IZ\iTI|t@q(A0P%Zk1j5qKV7yۻa|Emx\~!b~Np8ы>c(dƲm4sŎx!׺Yb=4q|`SqSmӉ| xy;c^9 ~=i}އ^6l3T yeO#,ϓR6(8^*|*^*ef˟7|?8w?_qdk3Nߧ;N 8\A+Ts,:! eܲ6|<05PL#pe}+jqa f~4tXlFpq8+㼀1pv%kmK>Z_S%>Wu\yFg3-6oxʫ!)fZIi1vSƹT^U_UMr:_:_@:_u:_u35-{' dy4z1d"9t"9DsDs݉V\ <*naYc[qaUH*rgD**\q̷7c< 0i p^A_ӲBwD}=sJ=3i 2Hq4K.1fa390އⓈ'F >4h6G}`F1h:>-\k^#kИ2>4x>;y /l3И">4(gY )u)]8_o2xvNJowdHx?ּ_l|wPtf*ڷ!=7 7n/Cc <0L_d*H{Bz(Ǜ!a>]<‡Bz}<?f!, H'#=4eO!H)YH-9H F7QHx Hx,c܏x!Cò b^^o^Pa^􀽦ٌH㾓#=4ToDzMoFzފގРMiTGL]Q6xA!=ǀ#=w!=`6Cy\ e\UOh:EUyv1p55ҕLxZX :f9+{\n{-bN{& JaUaaUK]3:#[}'ǵQ9?x#HCQv"Uc-{t>oFkS1MEZӋ<] Ӱ2ijl"'&MA i]C!3,6~AaR?)c!.L6u+9.Cʯg!a6O^W^ +J }WYHx5mw }__AW*).WQ(w|FE=KIyI yܔ~ lse'z@(`,C_Wz?tsL ntt'Melmtނ$v.);l:cKx"I w)]HOsֹ=pO>jy)+-\2o:7&P?I?/zwk0$7( kQ隞@7w)~o.-K #ꗬ[#@\Z<^k l6$Pe+nq3>0Ez% /HnWH_u)oR/2$^~^ ?kDZKYndxN8'EԞ廃gȿ"KNuWhOkxOkxMkxw{1v?Ay(v3,eG FdG,:CTo;3pa±y!-3p|?>78~{憖eD3~/8}f^.i,sBr،q A.G}dl hSLgP(ɵ;PYkU-}yd\mu+3AkYt[2h+,h^/1I_Q8XhoXk^U o ۏW6'7wx_A,yBY} Xwk.K{DzhN.K04`α^vO6g^׋Xob6hfK̕-POE 4kQ.V޳"n_U&2EQp 3D}pwRМүgCNauW;3GɩXx^p$3Ї <0ҧULº96x{q6epʹzOET> a,:Ne"38;=_[Ysm,Z1 0) ZK3/qJ㑃?J=Qw7>aK]_G+ 3⯉H˷&nF$]!g_)"n5(&w SҗVeH~O \"7HD]*.T=q 9WYS]s =   *KϫL<2pS>fC|&|n>\_bgoҙ%8߃}/C^qޝt3es bp(c/E:3Nsq+E"Zg9JQ~Y9zQ{π%SW%Ckk9FQzbEV((ߏ+E&Z{W321ڜ0Xw3Ʋ!а5x_7[lCȪstӧқe- _t1MәXQ8z%^;Yג;Hfl$X}P}FTymsyuN!4@ӝǔi:| Z2qAt'Í8Wر=w\v;JerV0Wtҋʽ(_Dy!{ڒ l>GCj7"nM5nH?܍.]_!t:< Mϥ3qE=6'cU ˜4 b\8Q`,Px8c&kPb{p/xNuZhsO0\` _xw^g;/ko^u{ m9gKzcvO*ps-O |Iw)H1]`;ZuizgAlHLvf"sl/ho^3|IG%| Ƨf/K罘<ņ-x1 t؏@ֳ2FŴu| "b:2 <c9K;y,ǗiPxv>F> ӎg.%:[ Gm~?#LG>K6ˎLlHqqFe/vGh0C3f91_YY̶pDz>o(NGؾ.C`z ܀XKV|CYak.a}p;Ʊ-:k]#x&%\ =s qMGidǖ|%_>/vCg<@x 7'D1V,ѶI|G TL3ڌ[`E/3}R}HzE?,u챱3w[Mք1֖'C>=4c,v<':ej%VƝ!Ez VےA6c>=ڌ%A0vk]8C]qCmoNg}Bѹ;k):imJ[kFO&uM7+z;jH&^|gQW?t uD9>&.'{}~"ߕDIٱ ?XJVڟTf[`͸ɺDR-f~69uω κLGYdOe[k^ti/B }`3OߗQ×8ob(\2kZN{-=r)iGnr#wa9)tT+hܷ+h\G ҉g ]h þgy2+}ƯFt_ϭuX_xJ+>pw*}!k\Ic]?֬v|y{Ϭ@sm%ǁk'?dC?++پ O\*_neR6|'/mLIVi>q2v;" pUzM1O{Uw x***x1uZEg^AHj]p31;gkjx'=xm ;}Qx *Q}_2F}ag.O*x$j PZR5+ywO]Mq[5Vk{ch;F[K j|j-6w-K:~l,BjÞ7p;#Vg9Q6վ.nn㷭9GU^W\+~cWVS@YIv:_Nv_NQEQ;k.F첆œ\bޅ;Q>dqb6@ gr~0pC|i,"1+cIY_vQ.U.X6XHwj=+GNTrs4w;b[?a ?n iIAȷǺ?.юgͤy=dz.ckE}T3HAl:Y$Bi@Si6`mcQg(>s_[fBÅ-T||` Mcw+OW`.OLsoM,^ZS? tj2aCk߮תخ`eďd-` ጱ0U߮e> F,iځPGq3v;ϳsc cT/3R vAq(xKpdf8?c*pNN`<[vCh:tϽx f;HMV;lչ{:,ݷ]&er=>q>^i=c^ɇwc4qwDLk31_xc|N5Vf̕Mcl+"0r;1ڿ&0215Z_q}#}*]% ;G#4Oy ZylUZoإLK]KʣdynΗtpd]Npn]6Gx1H=>^V߳:8h!]y|\cۂ۴- *V]qߥKe^"w9nq|O%7xz2O\(xd>"U^D0K<]h}"A3%fy|8/w~ux '`=OLLnzp ؗ5cznc| poofP(KQgUAP=5TaNhNθd0$-k{d5}UMvث۴{E{W{u9K6JY&~ W{;'٧e~ y+~C ȿڊ({duԗݧ\Vp #1(׋d׏Zsܰ2ڧnO:p^Ғoi'XxN}ܦ>ø[k~ǸNng}p~Z߅X}c9^hbx@a)x0bT8![p7?4EzoSyO9to).jZ׻{hFfK,Ӭ% y~ܯ..~d:dwρbBX v)cAo=<&-moƗ 9@qYyrޏilmqa[T5߼i|v&wV6Up*;L}t ̊悎}9V`/A@9hAm7R{AHJ[7N>71NXXuy9Hv<sC| ͵*+hn4K0PVگ1 k,uw((T<͈h?庅2A=Ys ˴32D|q5wќ?lnӹݍޫdI{,,A،~$w h25@%2qNq^kya:\͇^:FQV!z8\bH-cHcHƽcx^.dy{5Npp ߟ,}!dzTpɺ'ip{vX8fS=W{Dݓ15='Qew uX/xӘ#ȧ)_?:!a %ZcG. /_{&~dAG^QW߭EZnG;e?ώT:|('GphN1#(((*(Q-Q]QQQsQ8Ǭ2oix%/8,Vq +q +8U8q858588u8qϏS:AOP},q{'h]ׯ'h]ן'hd Z;y5gNК''hyZl9II҉*; -»I+|KO ƝAN 'IO $I$HO "/h9Mo~x]&-ፊlOxXx㢁uF> Sc+E< iy3$3$3P-zpol~:c]d3b^wy7+uC%JD:hrV:JWv~ůW(j v0Ν!9\a9(VQ3.VkNATu36^W8y9Kry&{}9IҞ(yP,ᷳTr1,3R;.qy$>G<2#q{zx8GmsT9jK^zy #y yS绯SSS1y_ ^] ^/zk@+{̋.HuEK 6Isځ^/Hz؎Hzأ}Hu1 6piԳF;`aK9/DZK%ߤ%:eJ[˔)m5.SZ\ LiLiqv2eJ -J[J[+.W(mPV]BiqҖ* |fk9%[iz!Όq=1! PWu=^jz]:3F#=p/Qw*zGgMu= jےݠ- j ߠ6 Kܠ m.a 16 !ipqCEMλ#ǞQknh]j2*Y~ }fGj:9,Cej*S;)-q;U;$W|ܟY>ӃunCZWCܑ`#Kw(;=JG{|=^[~ڢ.Gm{T'#=ұ>>؝=>uV SG>>]ؕP};k<༪{qOc%9ihۀt/?}-xVpQ9:sb*OX\?n RxoƱ71Ǝw0'_e RsP}() yȺ*>z7`Jpq[8#?༌c`l_qz7nT*z=Fުpc-lgG[Hd4kL~pZ|<  D)рCsZ@4 >f0c v7v>  x jsF^6-7>v؏HX~u<-:57ݫq7 3}Dqϖ{67|J >.S3c|d'UE۞h=Z}\jbٿ^r?6ǙV? yk:+;Q8jMk{4u ~K[q 7 z2Ʊսilwaccc~q 0V1@}ڲq4`*xu.&bL }+x̧}/1,Ak{Y3q_HD e5<.;g'_[*L{j2= p J~kZq4=irvzmm={#]# .Op’B ?<;-%~l60i#9wbC8c<_8 e%_ݶ`c?~ [1#぀{3x2x㡀72XIVZO{q%T^G`ACi~tN\1eG8qaÅǻ~{Fz'a3qAm0x㉀3dxa< ܯ2 90 5(^ x>F] 3sn sšgF?2s._b؆>}Jt>s |j֞cg p,y&L cUqR|o=M-E,vnl\ꭻF&bIġ88w*GE/麊p]{7w }7f;={ :5{DmqO][m?0 :< s`QyJ&d3ȍlsLH?T7Z/0,{mqc5fjs=G񹞣zSpFdV/}J3JB x +zxCXͯňv?E &١8 0fҾNew7qe(ET7T^: o}Cu*;Py=Ϛ|Cs9oh9[CN-!xKss;C8N?85{8xKqm;-zKsE[\4Wt-]yKsE\ѫ4Wd"w4W}JhHC}ֲ؏hq3zonu6ko;Mƪw$xO;9i1h'YeO*s#SNyO=)w=)?do?)A?P|~@N@.@@@p=޿gq}c}璟}焨A \bEA~k@0]Io-'h.C^\wB~Ii>$"\ҏ$I-2v>l\ȏZ\ =ʞ jfg~Fm'%#흫>NXklmf8kXpQ3f9`9e9}wF cLWOzTO>ZwRgl[BJI]!atq%׳ թ-K'v'uFK?`[+f8p1)<`lWovncw |f,ݤ_R %o؟㡑~vTIp t5_6BoNZ?Synðn줓ZsPub?X Xf\ҁ0p0=+ax8gŸ# MX>9]!3* ;d pO#B!Dٲ?{Zf< Fe~Ya>">g}o1/Sg=t}G,PZae'A/k8%ૌS85yN 8)tS3N8/0%g\qF?17ƙbYOc RoeQi +C=_FN,m^h" fN8]VV?JL>8~\p:\7XE$Z^&ܐݣEmZ4^҆c?q(x-:Hx#` x`ԃ/'1p }|xC= /8AWom${6 Cs2C.\5?]q~?1p Ơn3.cja{Md e6qQCc8=K>YSq=?i"ҾA1}׍ڼ $!'gv-uoG= 2~)YW3)oφ:ٸي3?g܏1붑XE^ʕM] Ĺs"g%h3m{~rƸ)DX9\Sm6p8µ%e3-}oLS-#>&qmoE}&ekG1)It[D$I؝$2f{8gCY)f;lea{$ v?)gqz%*~h@6O}Dm55 Jq2OctNgNWWpEAj2 v4{c/]Vw0uj:V;ߒ굓&,c&]8AaH=4c&_hqhktE⢷(\ٳawfmG,pSKObD0OJ:8}Q' 3h{t"-}j\9)S̳χ|1߀㼬B|U,{( @ӗKHo{ƞ\>G&9#)eI|nMj:dma oR^BR0?IuCng[w\U"1ذjgLv&Q=)x>%󒢭(sHJ/<‡O flK=ey҅j_9T= aPCg6XCg1>V`\7羏|LYf\ zҸ@(Td_1e|)|) })R<;R:cvOq/#|Lę{5[$s\hO[w?]:=8噎g:-g. }Dz<p CF6(nܢTq.Say(2]h+x~8Gaԇ-,+јS('1έtO+Ա8AqĺK1qiw*, pIg݂*3~ "ƻ/c| 7o@cL[=\cQx1?Ƙ//ʕ3cl`lctb@Ov|Zb}BqlBhM'Qq=m ij_S{PG)q|$#t>&9CՀDŨE )8Wbu4.H54ou*{@;O9nzg &#})4Na9cϴ=6Su1̟ Lӱg?eQ~џf'oF`Zgz`~!fO_۸My܏#IEE|9LcC>XWtz([o+<=T&[ӡG?9^3N_ rQ2u< p4mXwvye,GkqЁso fcub!179≃5Ǻ'/߼цca>>!6%N{ 6="{ANo{.a',K^><O:׵i!A˘:{# hZ"AT+Qyo@AT>;#An "TIst&#'1僭èl^uw׼>jc`jN6YV?~9x|{[ p Z}<X:/Tg}D"<\T  Ͽ{Zz`7ಁԏXk{xҁwy!ʡ`s7dW`||Ucg흜da:qesN 78lc91Ҍ1*1=ƊpkEqG@}L>.eqx['May*u*_ \ScalS@ujTV~@ujHpXXמ0 w3*֯6h56Q~C%;Bot.xk݃pNzM qq@'(!gRf& VYi{A ܒZu=q׃PmHFPL A(;ȯqq J'&q7aW͸ _F\# Z_PlW(ۈV>H ߳L`<%8SޱG gIE[*"y8e9ܦ~6/WJp Y}+q4cLC ;Wm?P1YcCVZt/Ii'2=-Rtf)czc2a]sbCC|eh8֒EٸmnCLC`-_fJ=W쑊STT 4OE:oT4GM*tO1?&3bt7hHy#iRq+5 mg[*a3l5~T/!!PX=rq|{2m)8>xR@=F3m @>2 =i#Qv8!E~'Wd7es!L(4 r\x&?e>T+xJ@}i%7&G`@˩sU?Ҷn֝&n볥ֶ";wg҇W ܛ:){fn7mH]fl.5n4JMcO,Dl?hr OpߊkFT/)*edj-OnYqVvگ⃶Q}=mE7CNc7 8g-U?Ih)hyh8Wfxʴa>ƍ8=?KCilHd&qK\/˾4Z{'hNˀ A}C }_Ua~X HKag |u+F@+4(RBHݍ}э^J+1+s pUN!չ< &-ks7:?\Gtрǔ#UG9E;Upt=κOV\CtZyoex+! ob; ;Hta$Vzth/s8c妏PiteHsU]S->OH >#khx#}PK>e-" ~JgP),Y::Mk8~mGïrwS{)zK }6 cybucLPc=EA R ذZIq {0+0y 7궁crwQO3nO8 ~/lƠqvq:jS \wEp ϶ධ-c̋$QEd(7V,ӣ=oA~}n*б <|PGT|BCqF-h/sj7-sIfP#Ӝ1WRƘY3 'Gpzaec]*R /E#T"XVwpHeirr3D'WO M $ity<~[]]E&pm .~*VD|Rpoe~%6[ [r|@$N?N\6@׻;VzxڑqZ\p}n[L *^' 3?4:1}r#MmBy>+adF0ݞW?08y M ϑP6s%wf52p9NiΎ&OlIxypcS-bz%_G]d|,ڄHYL9?ζ0e-i3Yo$yLԺrz 3a=y,LEÜ"}=ot1TwMQAA )2hc% ,ۜ3x*>34g3q۫fmx]A.rqirM`8"s}Q^~MѠ_ ^gz>c9T:>dگ P[7Q՜žZ?Տ}^qr8`}i|Kpw:6z QCpTahzJ!t*ʃ9rLW̤~~ɤ̤Mْ9Ph9!?9됫I̜_pxFrFfmO_ D}>f:4Feuh6ݎ̦<f:y&h=F)=FcXEcn09W<]a}8>`|fPoT&g'GSdnGZֺAkݾAy_,sֹAAQν_Z>:3sКe9HoC9\ || o<_3QNjSoo*{V~Ii뒓6.'mENgş2:a19~K99[qwbEC;[#ڃLϮx!$e:m$سb80nEhCZ$r׎z7gǚn[\zE\zG\zzZZF2@|(ΏfwP^̥ǘ>:[7\z$FԘ%c}O BpµƹT\gcܺh7s_!Uf=|Aٻ_b2^p,M5n^<ԙˆ |FFyb<=87a }~i?ucbi S\as=O8Ad-_%s:S\]fi }'ְUe|o~ɯ+POpPv>ף܂[1o9 ݒyM.) zUv6=}9.4D [ٴKknhbC4X~:#ϟ4B&c(K}cpOZb5U^F*_^FŢ<[m3F \f~XWp}%\-dC119l/L@+P_Å A ߱~8w\fcܷR;Cv܃˺>^@!nܱijvƾK:0 ~A8qoAe1VYj<.*l1{22->aYHE c!pk ^ P]|giQ{}qa<׫@ S9۬dzM -Ks^W8 pO=ifo鳸ax7[r,mGr@zڬ2p|*=3:u$ IF  <mSJ[ܡڼ !AZR kGsp\pEچUSzz=;1ֱ//)ϣRpƉKh9 9d*Ar[oh:=#RB+hTB+XB7E~SJ'[ׅ,A2~;)6c{ ϓeSb,I;h /m)%OJrG;K yS.\rkI֍g mzb9@+ǡ OD=|)Gp5=NXܗQTC.+-Rlm.si= 'pnƑ09q{3x,cRYq,L= G(?g~ex8K4X-dH2,s}þ2TIexgwQa-<෌qPg(s0X_ˆ^mc1`.W6n6jj3Hᓞy`;Mez~|XE?jھ5o-*[k=]ކ" rHm'ϤQ1yDfE `#ͺ ";OWhS԰oC9|hY4h Cr p \\*{}4(/}n⊦շ` sCc쌱WBE}nu僨mPim"Cٓ`?;13D}RvlKE =!=+L5/_.p5p1iJGn%wDzGDiW1OT;T0ʯ*VAv_Ez0{I>@N/F8__L}E&A_?d}2GnD}tf͂g\/Hu !==pgv>}~裧nDrاGWf>Fz/ y*=0 C'1{9=*;{4P-nU=+;>Ag/>ggJy}$gJq>k?AAgW*GW8u5.s2>`%Ϫ cնw# >k`\{b0Q>4IAgc󮪿Qs M YC|Wc{.7ot<\y3otrN6stޅ!_(3lo/* &3 WҕL ](L7$砐A9ǮiAUt\eZ*'C_U .tz%L r J@۸Iu\VcOyVk8d%<O CD6ҏwcDۢnc3hPhfQuw<!*xV8`$ڤ?D>(yte7\E^/vEO|m<߲zgK 3o e#ҍgׁ =@F 2V}k!۰.ɺ<:6bK[!;M|ݔ Wm(]L%Yv''7HS(3[_w(_ ~98Q"?ju}&zmznFݝj|/ ,j  }M*MG?9O˘Oq~WC!*g+3ώw9;oV  A׉coAE~|r5"-k\W_ }}v?S~t_RopnMϥ=gse9^-M.uuA4[uf.BQSe3pws~,oPS MWb&ifGC9 Y{5_Nw mJ̷s{*acMM'B=v_xgWLkEe-|hoHx-R9{x_z$pnQl cHwƐ4σM)R?ep)gGaᾉ2~CհdZz?Be.ag(^hu]{Co=>zŽw+gxOϕz%0WYٯ>Ӛ:;\py3(N<.4*-{wx]H^SPW/MQP2o,t UսR}*Bx*t2Z3N>=>kQkn6T6t#QC=MRԵ=St}Kwu AFPgG7|7*{ ~\ >Im>n ǽٌ~@ M{)q a1 ` >O7viՅz S8K3ZѦ3c2tIcq:u^'2c ZP.q>]-}UvglUG03kHzz?1S?#`Sz50# }V \xMv^G:g3Vio +27r8s{cTAeu'2eqAx#Yn(͵~=&WKȎ:T}ǽs:z%s~ܺz\H]o䨫[JPtm[t6-뚎8*ux6 وJ7Ҟmt.WWlSsg-QeWϱ٣d=Ϳr=Ϳ柊N#t0M+DZpI:;}EӘCg{>szy}A[m#-~p!'8)E}_%Q׋I)Won>瀛1B c%>e}G[֣ d`\ƙod*vYf\f*zOj.rO3_J܀u /_{iB ^c"j[SL4wRXܼvKTc}Vsm1|İ⺛IDR R`}O`mB|G|~Xv먿㗎/@NM<`/"?w4j`bs!Fn@pJ1,p[o@› ^%:SƯ!)!)!)}C:SQC:SSC:SoC}&7$diC:KfGC:KpC#_hPt߆Z'P_5Cp\lsA#=K]c8=[#ogҍ[g?sՅ_#XU0Uo|nY۞0(^3FM rTzs3҂sV9;lV7aшa~oLɿBm' jULɷHEBQ6ќm>f?8W k+ v#}*ڣ'MCLc> 6T~+aFڦ~՘' 8.>Ճ GǠDyoVUo툷1'j/DEO^GφRviBďS,#JvaQc m%?U?_Y`gƦe֘dT1ɨNcQ$IFl656tcjS73P PcjS76uHjS76uG&Ԧ>nBm)M mJsUlJ{~jJg:kJg: oJg:nJg:lnJ{~5}9ҙޖP"hZuYw-ާ~H ޠK zqu7s+4r; s\5 [Ghlπ7 {= 1WNmpS oP ߐ &̄wbnx'u B,!~B|=WLoG_ Q/dÚi5 /PJ, o/po? 7o߈t~WA֥~tW 7FrPXZ0-bYf[|Co(w$lI-ɖ #[yL֊Y]ѷjEmEnlE3+;5]5gk5?֚7xߞF5mtoC|!m(}76s-+Ֆ5F_n̴ޑ6mخ%v7[;[ҎnkG|o#7ķķwMSN6Sp'N~R{Ԟ/1ݧ(~U;Pt@@cÌD)9:R85;R8:R8;R8:R8O;R8):Q8wp:tpwpupwppAr>;6:;egO):SߩPz:w!>c_ҕJ+ٕ߻O;ӟįįo؟oٟ$#It#Y?m݈j~?Ww؝w'޻;ӽfDŻ\kރ 쾏Kmz3#qO >'I}'_I}Ǟ'gE}r^'E}r^3bC/6F{o1^IA;/z:j<7aV6m.T~ v?ܜݡ4z^ZϹKߖ$xB/j.0q/+ FV7ϫ^Pޔ~)C{SfMyX%ʴvho )֛xoolo)5Їt:}Hk߇t}Hnj#Ӻރ/ӇlKqKe 犓e{79cFgt&XV, sJ|1\>sA~`zf+;1,86v\AF;G>غ0Fv#<}C8CAܗܵ/}s}y>$Ƹ?=%oOCVw A=x53fhk62ξ4'r9ykަQ@8^7՜wX+N`؃0"I\kVUc_8ڎ٦nWmg?0vt(uH@V+ -?b7;vnls| i38Pm@3_L4/yƲ2;m"CO=X~^LC°e8?cܧ^1S;BDb̃n [~OaOW q%ʥ\{}$1Ōlca'ge~ f5Fc3r[~ܷ7u|y=i<7}'7E { r@W(Nup\Z@wF"ad֗yϾ`'}\..UWb>GpC:Yh@ϑm6mCmVgzLpJLegJT0&lntL lcY9>Ch6k'#Gmp1wFz椡6 mt! mt& Ou9bIJ_^iTpW1G{z:*n( 8f7p`+,YfDyʨ(}ƈ+0H'GpGk?f9B:W~ _PМFᷟ"t>Ψ }MG¯ > ~[nx%\޾ȗOxyTa>~'=*f^qj$ O[,,hL%g>Gŧ貒f(IP>s(Eq"QV <1s4,_&lqqڇ2uu]]Q9>>pmUuwC]:=\i]U[R;Z7db0YK{i^0pI;p3zM)͞ڥ<*YS)I[vTD*D<38?1?d۷G#/3A+_gtC#(}GfRgx*]x^dG?xfIFqmy~p;RE泏-Ru0ǧ39xEs"#%+!|"1^w$|׎siT\y-OTVǏ_Mߌ&eGnm|*kQw~/hJTp!ǐctӌbcMXck6ey|^1sleǵS&Oӏռ+% cw_=sN>=ŴMς6TN=EY˩5U?{8)qVDe+Iv/Eqkju[x C)\xbx=wOMv?4Z;D$8iNGz;WqLrFEuʎ{ڔ؄1\P79g94O稻1-]x} ,g8G۟uO 71ک0nxxZQjY {MէIQk~_c¦!{vLgK!K?`1( Y~e6$B.py杤ySYlyPGo&^o?fsY5Iѝ{]aلI4Y-xvoNٍN|Ni?ϣA:'84Lgo?E7Y`NZ$gE|c!^dK *|kp3)%BT~䊅Bh>*b΃d@ӃB 9)<֝ykx b==<)Z1ʯcόQ~/p?b3EXנu خPI~UXyzmz5C…) 4u)\Bm}oBmO$Jx˗/|F?PvSM<姲N5Tè<.Jq84p}*9.Ӝ}ltx8ǥ c˯Grk.p.G0B4J~7NwLsi4tM$*)}fϿM .uzMBat]O5D']7*?/ga@g]x # /N9s$7y#ۦ) Zu *~0FY4ރSOW{tOW{ j2 Y(=cg{>(y=[Gbx97}5ܻw޽HGEca{;S4l5lj&c(C3cRy 9V(9ޔs?ZwB|P{^Oy#덿!I>Wc0Ԋ')Әg5;mÌ3;ܸk#W>m.L홞ޞUi۵<cRZ }рKͣzz9K~}yz͌,*, חpg֫y2T^0Gv0l'>4Zw`0b]靊9{qz;**pzE x7 cLo."9?APpjNoS%1bgKh8p9pdԖ|2S jցEeiƸ:1wAjo(X~B':g_Ly1չ\T,:轱=YC~Y+"8~sb, ϳ< /˗L]Li#z@۪?U{.kԝNú9ʏMDۆkS%XTGE"c,ʔk߃m4oJ LG2Wbb&b:O7toway.xZ]s?{g{+=2s_ޏ{!1z#AUTVPH-AU縶 uaYJv۳;/%!w8׈zRBȮ?j{q Zzu- z˅QQQGd3L8FfhfhDޤ3-zQL2M:Ѣ3-z:Ѣs-z:Ѣaƻq9l(C|)3[FzLSL#2=/7ji"٠m%x VZz,Z˴ypE\"c嘵ۀe[ |fffL_llTQ3k:[IB^W#+ иpyXnOQ>~Z%^N4!ˉ&rɷPwou)C]YoYcŽr: /~!xu^r ~5܂qS-\n闃 {.wؕ(g% hv9h9Is\sVIMݎe[D^] hfY˵=m.\N<x;`?V·5U_-bmIˎtr˴e?.ntVWy`/)V93WP5+|[Aiξ!ZAk 5VP^ZA4\A4_AtZA5"Wr?vwudp/D݅pvWDmBh 3E'_<4BFt)A_C"=…,_x)O+{͍J mCVfݪsiVu$li Žjx|. Wx6M6+Q?~Wj9>ױ9UO?K)޿VsW) s5J¨gͥxvXEqZa4Oy~*'4YC[Rʪ_z5G?8pƅM1oXq}`urtqWm=`ʽQ%ǘWҟ`w_IS:3L_+ϩW_`^_I2U_F_xWT}nb' >n| 'y͘5j uDD!}1+OWx+WuWxyr />}ӿ{rxe^s8O"p?2MNLtM*' ~K\Τ{Lr>G(^ߝ=\wmɺc Ž3 :-ge X4x֓czc vg=NN'Jd ϴDbYz?\ו^ߗ~^Kn۠גl0g+eFەc" E{U |I&eMN_،~t: NN y|BOkN?b8PB U\A l5Sr>/TQ& !.w@JdeFՊAÈ :\u#n.O.ʓʤ";Fks㧐n芸)K{1N3&kUnlrSeSoDf&jfo6i&jl"׉MdϽo7=g3s3ltv4/8|3ͫ.LwLypi3e`3gv3e2ɹlL~Bw4B:l1q~dROڢn! C[XGr qa޷#K/uJϷ}*EcHVveh+2Q;}J3S?0R[MǺPyaEp6ׅVK.oq޿Vg.}?WzAzAzzŨzfck ڮՊ۾UQ=[\olnsoHFmDlE !jW''9 ۴7*L_{UYVlۄbmVd7czқCYy}Gr[yFe @T>{a¸MMKn靶ξߦmmsV?C<2O3p,Ǹ_R7#f75?XTڮ$5" :Tms ֺErgzLcߵ}:+зLT\C/+*?(wQZ"!9:m'ƃ=Xkq*t]"e>=Hp;M~:3a#Rtc=j-x yS!"|,C7qW%9L?+ʛj0k7x航:)xo2C;;,7<u|bsmn>pOSpu=߃~UV6INҫ&uzay(3 4n#.4!~q { I鍸< O٣e.T+GgV${?G_7my=z\ *ao?ߜ~"b5y\H5=D9wՇ _):x}{Fl]r/ 7ko`7p8A3 ]ozQi\S`a]s3$yye85H4g٧UʳO˅YUoBga_h&+2p1&ʶ'K> w7?IL#Q&6.O@10qͲZK>ao4{#ϓcñgx6|{[A`?tw7HGb_}";s\qs4گk=5`X;gMmHbww? +Iw~>'ۃt3PxА!isΗt?1g~moX_گ禶sS 8 < 1_{y~םl8^/.s=0-U;pܩf4gj=TP<8DŪ/ 014Sn /sJ4к5%۔a&UD/}HzO/1{1w p4&7 o{epB3q@_ 172GU4e=l3;,7x弞1y c~(jaV>h?%[dkhߦ}q #VXP~V2c}ئma&5iIDp]4Ca]uh#1F^o< Y‡^ozL~Ӵ܏TfYtgetM̜B Ovpk}1 :fPuO7LФWnY #S -t%0ɢ&;nxxҫ̢uoKA(Hp= 󩒅Ҏ|j3V᪴7es g[p4M,z}h{H}f8 2^;y+ç@si6|.4'D|{uP('E9>@ 0p^cpAƞb L˨C4,a:ٲp.3lW~Rk+nL-{r9=ȩ=9SP_0ceVg C< X F.MO5!=psq.kP6\fǘ1q?ĘjnߚTc9.%pVE5ӲZTDJgIQc86[ q#7t (GoTyq 渫hc-#qjq(䈊a$X'Uɓ4ojMGxcp哱)̍#BO1Zm8¨]݃F/G6(QZoQZo9(\rGiIrx?=J}q;wxtx>z#':#qQ8p\߃0iV0{Itv©vr0a mKuhyA=b3%)ػԥN)I4vĮ"i8l;=T%[6Ҫߞ8z4˕NRހoV3=eW+f;Ik1F4cl7kS hOAmonSP?U$Rݳ")Hu=_dc01YkEzȈ  X?xQ _2׆4{n#?ҁLNݍa3^ѕ C kG\z _3NC|F&gLWm(k\*^*mdw'iSuR?)UL(ӄ.~|G3_߬2.9nt/tYwsBԝ 67t>^Akw 1Fe /E/i!i6/clc^mlXY5~#p!ǿ1+y-{CҥޕghJ34? ΐrڻ ]vO'ޕ[g. ]Itt߬Nv g)Mg೔gFTECI}1ۥʝZߋ48*/։|Y/0McMt]H/R/]3Ӹb5}qXu~;Y|`y##%;t:dpsZgswmߜwb}Αǟ{?gn9N@3bɚ7ѠB,99m>u>up^}/C#q{~J:jm˩sLPˍZTNAO*]3w]N Kx4elE:9t~vyH.qPk& ů˙4G>|ڜqz=cBo.SٜxT6y<;穝{sڹS3/P*@;A#,P7Bi@nF+4m+ZScPLJ=-~/{=COVc\'US|U*G > ܽ0i1ϟ3nO|<+q^;x8]7kWϿyOjF_,j{@5 zbyLgf4cm,6,/X'qy4︼csQ{P?]tMqMi84ҴMk C _<6rQ=~ %qT$ubyi i^VH}<1b8cO9uN; Oހ2܋ax>Dxm8y1tؠRF hqN8{@ )pP_\sԓq9ޡT6N]+ʆuY(B cmU( P*n車p{ZՋo:.j>^U+_ ~k݈ۮ; 5=yCr{)nh{+Ҿn61.I~k> ~O x jȏ /~~z3ўz #a$g?uCߗ䆞]wt7p)r7LY@'4O ?̓,>Z 'q98wYs!O{J'| u<4M{ u>pLnF&uOH͞M(&py u0,4>ůCMV[,sޤ{{8>)^KP>3E^ֲ7y)ЦmsmH|~ ~;H~xh[pϋqOi%OlwI1̠ B2>0] s3Oå #hrzlBq7:7ڤ979osW3odqVoqf!njʼsEȪM}a||MP9l4\LP]>ޤ(Zy|':3yr66XtX91n%C)1OKqz9>YO9/q7 gwA'c WqG0u&KQxRMDTƣn]m7i I[=7q|JGqo3XʤK4ּFKc.8RE4n HrK{[z?߷t;VRWUEYABt܂rw. w2d"~yw𻕐unZ'Ա.L:vks|[|`<:K{vϧ#ho-MԵnҪ='M `0om7Зvay{6NSyQH&j,;^~tmp)+-m/-ߙƯs?ao m=16}xTv0nk[M fRØ ]&~+zww%FO=]缣ǰauơ= yviG0ͮcfbПL Op)p/И1$cʮ<*Y߾;tIg5݀D@@EYE(@ApF9308DTy.7OpDfSƇ\YNH^խ:Tw'/Uuus_kkݗ{;53{qѾ'ig/з uZ]]h3D== (:3ir1R}(Y?W_udV=LػA37d8?XY?^*Ͱ˯}*{ڧϯ״~}.A>G_t+co+追H+<N՛m.&>k7u`;"/v>\D{Ñ8>Ω9{xh>}U;U[DZ&Hal(fSܔbJßŘYAؓ㸸W} a,gEƹ.bgx9LW%z}NX= x|98vh :Q5BY]I1 cd #:i&оqaB?&\^tE16`,sNUU<3 H{;Bԓˈ̈9 O5ᾱVuw?QF-S }6YJZR9w/vtU)ٍJWJg-f?0R`)?({l4'hgwV\"߿(weEZNNk\1PFr2zOߧ'7<'B-,0ok̶Q["t/c uL!Sx2!7,0Ɵ*Wcz?e׹2=(xZ!0vv*;);f0sP״06<Z;}!6fǬ{򝃸ku唧 yrӧ#|'f߸f}>N l-} N4ς3EV˨^ϓ sR /*ږ2*d=QA9yB[*mUD{i9ˢx;][`udxqA*D X/ԺYQ&}E-}/iܬJGgfh73q.<*J[Go~V()?C71*"Y*S&uA~J>4ԏgxP ;_C4W1>*\1|Zօ pM[o< uDOҤXwFqoVH8Z R ?xꌍqo#1.> z h~ˢB}%+;__ ψ npzu>C7,*;#s-ߗELu_hf>{\{I |0mj^0b96?Hssnvۻ0qa^YeE{fM'fC 3wWS;_\M|I58\djpxkj,yfi[n(yiv?B{վ(#p1xT9O`O,;?&7J!4ܳ0oCZGJݺ82u8yQsWx5N0]c:iP9r̯rUCVPCuT S} S-ٵ4 o-gL-fg~->ZTK-4y>ji-ZS#a oRc\҉ |ShZ=iP*0fhc1/ciߖztwȝ/t{s 1fiк~&!Nl"&!0&r?D/ lCx^;0`;5 0ϨCnC3@;Dw`FY[}7#:9367S?h>{ߺy-ӵxe ՛-Zw0W[ļJu.JgVj{#Zunm%[yP+|dnj%z{yN1O'>5D;<;϶>^͛Li2mӺ/..P;M@w^ Et[}=6 $,L{h.|@6m6Smm,06>Gu~ݬ?~1fN2y;-mV|5v>!|T>S|4gzh9~ c,3 W>60G Ʀ#F626riIsa"UN*560V2gdުDJk["59xY ӈg8 i:~CT๮Iǩ9AǩA 121 ¾$YpnGl{S_7 Z ƨ%AWwƸǵ?c|}=c(v9зi1#v7v)~ Ljx8zZ ?Ճ5~|멱ŧmP{ |EA)zWEMoR?dӛԎmzLeBolE=[Eܢ~m=?b?|~=Q xx] .W/xd`+>. V峖 %d=Gk<p5O|:T$mfK!>ݵßBB|d| _?z-V<nk0#ͻ/=ʼ ƤXګC6q琛t&YT Ŭ4BPͮř{ZjnpP}w)ĭLy=Cy/rmO RO1}jݪhfxVx6X~k/C7X~=)_xA/O>/cz>朡 |oں6l‶4Fu P:98@uztlqUfV [e1,k5zemdYowXև,u2 t =eCضI_nT2.M%ۿ8JmcT*)x1'{ ĥBe" by!MT6oAsb Bg\~q T ufꓔϟ4fI~OT`Dkߌo0]|fzu`P=|a\wYN-MۼnyB&p}ׇ8G=W.@ z%φ-62J̹~X*Ө/M|=z_ӵz5KGO}-g q&2΅4s7dyO'Ss@+ϽX7ɹ7ɹ4ܤ]q\yFX \k%1H yck&7͵=?}ΞפAjpuxn3ӈqR|;AUiELm+9/Dmzܒ0>K#Ww(r/ޛNcjNAO~oN袐^pE/ݩjXZX{y.}PWg[bG5 0OV~rLLM٤roPcz(_3I#Ьsr\';YY /vi]"2Akt@Ӝ~0cηV{xV+ڀ3uoi>o2mm]Xe.L}ڎ>I\.MdzM"۷{پ)\uM܇u_^jo̲>bW>h{mWblEyD5'} *(<5K=Yz,,ȊwL\x-P'4xS#,ݎT?Sx&xv%Q7dr9&79 !!$\x,!*FuQT/tAqWP9T`QT\QA`ADTTDzUf}KO>-9O4Y}*^-Ne˙A663SªN9 S&|g>ʀxɀeweA/w3{vIY *N y19 ğe d+oJ [yWʇVves"噞HyV&R)щ$zǕ}O4#'[rno=|g$R?nd:02'JqXی_^z70/**I.D0Q2F]$Y?$g%HN570[cylVxoߝ>7҃})n+!4ICk!ޅ,r\uy9bq-1k GY9!I>s/q̫|f; 3ɧ3&/KGÿN*8/;~(x7Q4<4>C$X8?qb~}lIlM<'9l3T[ @ȹQUD5qV+yD^ csaE (=zz"׬nA^eA6LjɀIh8tş 2\ Dh܏K@?~^pvlۛ4/IhsiU~:Zo*, Cͪ/aۨ31qT;9ɑ2 `.dY[L d5=Y8gJg?v[^{A⸟ULVc\]*I!>wSn^Y)y}yUs"moh痧<"|*"gg$ҡ=tSd-հ(1ktSD4>c[=aw])TRxV Ig={y>E&nOv>Fw6;+P->׌JHSñ셌QuZXR#RRGRǦRoc+Pe,a+A2} `w$v,_?ַ\R]]uѮ i;FwMY_30x1>R.;ȼ^+: <_vht4 r!iҧM^=_{iȆ i[Yg÷i>w]/$esw?q/U7q"FC5[5r,͠žf5i(JӬmi?杔;ˠtEoeXz }gʤS2iLoI 3iaU&7lϤ#4ߐEcyu͏ʢiY6(f^9 ;DkM!Z!ZB,Bً̫_WC"W.\=q䧐gډN2%gS:tMt*E7f Ȗt:&N\Իc]6]2\AKvᎳ _ʖs^U!> :ݚ-Iǣm.4?aE3q9$ǰߠ`d99[v X1]%̋ js\Z][POǣlLD>q(K}maYi92hD q^hf\^mAᜄƽFgkOIi/O *Zɿ]9?߉$$rIKod./U/M`h}m&.۾kߢ85W(8seݹ\S=6{%uy mZ>m mwgmۛG6sҶTѶ&WٽhpK63/ x}nxxGҸ1wǒ0uτ<^+ƊcYa؜>TtPnp>Aa1 wTQi\21<MBr8t5_b+fOYc|Fڤ#_l{M6ي1eeOs#,E赮n{A1{X^3֢tثemsy+S9/2 d~]Q `P856VFG3VqE+22[] g#^/(0IUH4,$]8nSrJ5<\s34(*5+Ԍ4$Löi|a{4>Ӱ in!/59/O%Fw0[ސVaƞirėTwN>:KDz 3q_EdCj0D?(<~U?Κ'QQ{+->_SD")__u19x xa|Hv8)6BCp5\-8a[xa AB;JbeGߏ1?JZ~.ڭ,&~G y*с|/;NrUKR,{ YŲiYtqU848W'תr}da㌃8WM'{9sq[4񻉣0禨p)zSL#hZSEvI.;j{uw " ߕ|D957=eo2n빽} (?ә߉/%ʔqn y.s!K,zv^'$'$pDFeLω>cIb?斊חWվ!>޿=hZN;cX]l7?1T].pcԧg1[m7=gp,1^kTί~fv1dɄqNcdjc/cpAh(g0F?1vѤV>򞓙2 Q8wzbxc. 8 a>. q'X;km܎ Ph>8PVF"¹Cɲu"TF=*νj2>e0gBعed`338޵>Me2'{f|әANqA擀AT[WDl'ze>-#ѥə #{fE?3F?㏛@siQ ߬}haڿgڻZa9 *9rjOוK=L)qrۿ;U(udop;c'mh>0:8Kv$/g c))~+d2;!^WD S+dOL&~Aɶǣg+>Y '\V^+d/ ً?>ZurU9wuU*|+B'hcBl+,b.*Hٕ$zV*EV>!"TR>gVZs,?u)G_W^IO>]Ntd?V̋{q3|_VywG=@J[GL=.qIDd>/S3_Qx,Um3Wwnj ^Gm'Kw"MjU}I˧k?ӫin?|T.2/-59S !fjS} Tb{biʲ2wr@Z Ԏp+^?Q*5@[%rnLѲ`ucCjWP{^CZXC7א?5d ג(%1ŵ>oq쟞Л |C-qt>q1ݵrFeN p}xg1Ě|Wyc&//ou@k]eBsIx44NZٳnv|ľņt'Ď Y]Y-;W ñÑok8 ]1S%C+،}_u>6?c->.1V̸ Е.1Aa/ੌ=߾S{g ~~QaY _sbxZ`lX'\w䳱b|Σ5%H掫#;d#u$sWՑ}d:'ԓͮ'YTO:E|q󼠞򼮞򼧞\\OyTOyn<SHg6PU {B;Y У 77> &5>XH ")G'gx"+ֶ{Է[d,RZFy_#l$~k$~~Hٽj"~4?4?6?6Q].o|OH>OH >Ǵn5y=73x/=grzlHW?6@TRg(PZ' u,kM>+_oH|}h`߾Ds"4NXZ@Sc'Q7dJp7aD=ќ+ |fs-x޻3$SѓӫC׌! C]zrgN c7xuFff%6_S{ f$UeHW8qQ1lP+8b>>n,O,>%_{hSy?Wȼd̈́mV]o)[i%ɭeL)GzcWUO1- ty,"HPB$$H'AT܈ (" Ge@ܘn剠:V[ }}nsuԩ'x+"~ԘM̂v5~q#C{\As^p1cAp^c茬2u9OM*`|_O 1|efi^4fIЬ|So4͓Ac >6> pTgit0e3A!\l4+?+g`N<K&%}|bne4㚑J2wc_qFM/tp U3ug莋-f|@Zꯤ#iθ|ޕ*g{Rh|ޱhTWcrTZqه9s Ùr]%}Խ&o4j~*.~EKDuuirTF4O隻O4ZǗs9`|!Mw1y4:guؘ))?~;;2812W&S#8y1Rk{c,lDɪoA9C47=mt߀4I`ggNp_Kz\As> <2"DӕXy}֝Kc끡 DQņ~H2>U2l#_@3 _-b~|{nP<C )-)G\^ֳ5$dc6g݉bs"S/ c"9Hsŀ[]Miv?rrƊ<([YC+-7InnnB3͇gePg_'/q__̲ŘXT{_Õփ1+[yjXM>SFi>1܅cYrd5Fؕ\]iW,/f3od 09ltXﰗzqcه)ࣀ P|˕{,?O2'?1NQ{ܘKcñ.c"gKxinsљr_97SlK2}G|1K!Kuw2p?2ſIkL e\3X-KϘ3Ŀ-W*הLp{)wʔ;O./ȳ>v1x'SΩdp~8?jaәm'K W(\ 8%g՝W+Y11ZN,kPXV{Yb7VqqV%g7fù*.U{YR, ߍPkw+AOVg9ٝ3 cNs_fg̉[PB|!$a| e m:?32ZUcNdp`\}~Em2C6mͰ =w}(v惟n٢ v+ߦ3a4NOwxd JB$Ygw**!ԲI}7/dSM6lM:39tyto.ʡ͓rȡ8 \{s/-`>?VueyƲ4XrZa\ : + x_OJYXڧ|w. pNQ߬ϣ9#]ỵ}<~_VkW瑾vsmȓ{9iiͧ~v>qa>4E7zyy$__h>:߳[1p5ENFWVQa> {UOx{UOr\?k$_^u!jW<&DfY!; ,xC472^( ?헱P4+1_Eqϻ*^=os>駰v峞^ BÛ`ҳ3`C/| <.X{DEls@m dP^U| ]X@2uvjlt%3 D/EYڂl.>^ajOZ:6\{NX*S뺳Z:ϵMZ:M.fl϶ͨ2 D_dd ĞVBQv4 *0`}b 78 h{氃OKڢ xm wxacq{W-q/C6.G2oEUҎ,~zr:=~^x ]z6͍,L7 ](嵅ޝC3P|'5?4K f*zO ˿tc|J{I?VU[j| i,ֻ~=L|m*$[ozsm}qGy'%ҽP|ۡx'Օ=?(]Meޑtp>R(v5N`Q=nף%o ? GeAǃX$2r}CΘvKEwb&י¿- EGWbkHGu*y_ݭ<,'~EqA":},0Z)־~ާcMFrP;)^QPDyh_a6O}BQa% /-j#O܇vwF|=T6mE:iwZL:K ס;B}z?0} gx>cy1X~F.67{јVi1*l{i6O/.2)51LXfN]N3*8m۪is%HKLrs +K7W^)*:s& $繦twMHoƞ𮭄wTp]*w5Zr#w+bҿ(}772(>Roxڊ2iPeTetZۻQ>^Fu i4^4vs=?Y(3s>ܧ\<<,F#\/ \M S\Fغ\Yߊ<|ˏ}13M.!6}kc7"=F4I#TN {q _B?r \r7U1ύddH pz5JUxXL܁x~2!gj;iV*+s*dq8w>{Q9O:wePmS]o=%w檲(\p^Ll0bk2>"۶[Y_&R6qB 9wkTOk['v9 I: wTxZߓN >[A}~~@qRi#,l~~`#4h^4#w[^md>W] ܤ4uEU~' 3?nܣ*f 7Rt+ǿ9LCJٯ$Hn~ԟ+1v7-o*E"0J+^S)47\{JO .`!F1rS}8ig)newWpuhNO)67pL/1zy gpY'&RxezdG)~lp.1 ]+ȍ]ѷ<7oaR ߾ܳh{R^) kF0nĮ2{Uk)c(6U3&Z->nU{,Wu86*~_;Jtɳ8z|~b:x :]OA)ty7N5Ug9"ض *oЙL{0w4Ql0Wp<=S|ŧHhu-OkпM(T_X]M)jGGT մRM{;I_j~B5=WMFTѿj4m}DŽ?lխhݜ/1yh>/$g|1ֱY1_j1`7~Kӏ}>:IWWJ0Ux$Y6QM$_5)z{[.{UK{&ֈץhΫcL[ Ñ5r0ԉ: kC57u׈97;m9׉g*Lc̼gn఍}^SI@ܹ52^s,zYN 54^xoҦKZ/xIRZKeJ-94^ C,h: ^dW|p#` ῆٗڳwk0}k3OFkgWc8,w+lTq@jos_`jN-Gky-ե3Mr}aӸ9ܝDәZs\aePnorv`i|~T|Lo\oN τ2D6\ߠsxW9{};~I#=1Yl]zٸC6.C6␍{RM6K~^暓J30-+۶NNt9Jc_zGLq% kzzʫ] =Q@gy}28`^cX[tSwdǏq=ۇBQc3=SC XIDֆrP'+|@S gy5lLa0՝1l8|epi<{<{߳euTx |'0nrKqMݦ'q^XED%"~fGn"rшi-O|nذ+#6DxFhuZ'Gh6Bonb:Z':ya(qDg8` z: FVtΕL|!lrц7gFv>;lI"؝_whyx+=(}@q2ĸG@0Td57 h1!\ਵS˾ N~m] |TE3  (x-AP#@#!@ c WH$$&]oTw8EEUL~W]]]]}w~.Nqr8S1\?7Npg8?:K 7>4y͗_x mW0w˙3oq= 4qynEoHe2ϤϒVq_'|{~qp 1`lۋk"\gyeTe+jlA~^$:>n(Rv)l;jmG Hj7<5AIr}&҃r< `xۚi1QѾ|g8"PujO*?AʽC@5iE[IϷ~6hc>ק_`ކ?KvYq/}kauXǁ먎OAĘD#ߑJT LVBZ[Cb)xEQq kL <㝬+] ?oR SM)~VɌi@g }w40 rhʘs|w錳FpC??h-_w˺"[#:g}zvݴЮo㲅}|yc>wUiJo8BO߿acmϡ%v%h6uR M\FA:*: <[uๆ(seX\GEv]P&n x۪F|l]vև{"Ɇ=7x_$*v0g03 H95EJ /Ty]tRXom48^Aʝmv+dm[?K$>~]˻2H;"PӞW4ߵ.WuzDTom ?Ȫe۠GCgt pm^2ߍ uV8ű=2%x cr.]݆@{c3oI6)+~~._UjڽY8n~vww2N?TiYgtz?vP ?-^3UDSHϑr~4γ8 6;x=v(9c:!c*cHK],η9slkSĎ.7܌9gO7(}miCZS̯f^a;H>xngqn]!vcoCx<axc\iM#{uF<'ԬQ~2l!|~Ev{ Wz<]yM1V16/[֏&!m@+xLUK2|r-aqiCc!HS O떰 /^RZ8W4C</ce.ݪ  ت YO38$$J 47?zM}@sSghnZQq Q5㱚{z:|x127z'0lyEQ@G1oC6׸n# kAF86f o0<>cT'P奇ꗝ (=ܲ1R9O*WA,~~6'EVh>=gj? V::ceOce6X[5vq[)u k+³yX{Qub*2=uX)ȄfY̿θl) z QⒽ;ljs1!:u+SJ\垣U|oi[AwESK po+QZ?k ;?wJ^r 4 ~^XDZAH3O=KXvߣ^:ϩT yLRS6Wg7~p7#}*\\O rx?sx$ $7kD퉇-J"d mO*~~4wA/~kf!&?]~wXa@B(h]AUՉ$e/ '{݌mݕO{0'P>mE}2[.1څ+9nnL1B8s&tF)Q}>tǾ`Pse&x `Q8 .[uDnL.O&;9d?N&s>6O<Eq^^lGIgd44sG2< S:>G FzN6 S}~Jg,¹Vnpg;)Qt ]%nۗ(zcgg$Oȼ0^9/u1oc}_55z_zIaz7cLcRG=EymfcoG=KX)uH)?܎1ܝ1i^A,q8CSS 0bcpkOA.xh^rc?L\^߾JTZO`[iQ{lqEiEiu*cy1c]6EimwQZ]67S.1G;9J[k|zJ_=gkOgi3Y??,׈fET} _)T'2&ЎdZ\+4vP~vk؝>*9):u tN_/y;{XLIOI4wa;ISbgo{sYNu=CYs[}bwg+>%i]1ԥ@q~>Pudf;fI\Ih(ѝ![p V.Ow0ir/V w<3}~a>^浒9Y2LRbLj;#M c+ȼ|O4r`]vq,m(+ިp^V(YM} sS84I?wcod}d 0~﬩g.A|v >$S=ΚŃ,7Jo=zIsJjAq%7je:H#={wnRO9UK~YψIW8c~#Aq.s4zI]]WxVgOJnU [*[A5!7F:n"~cq1 ,VF5=}K@3`Z3Ӥc3w[`.?[76幢bta&iyZ?*?ŎӥfM;tJ^[pti3X'~gM}_eW.FDTP<#{]*)n#cΐaN$ͺXEwuqQyMsi.ݾbCPaa7͐ll5=Zqn& =Λ\Eɇ`04a>.3xyoŖAw=Su#37'͔Q<q8ךgmh>%eW{lQ0CK/F|Fu3$zԲ>f#jͣFW0.G>{"r7?QGUG~2 g8^X ^^I:q^JNI^H,%;xii_;}0lsG%(Kԇljozl2hf;j1`t92n }g/1ø)n%4zz0s)dΕX=vڐVU}~t˥n8%?W} V<{[0o #oLU ~Fxx^躔@7‚gZx٪78|o3&3sELx̒TF] ?po%g7U ?pQ15D7R߫UM++Qv 7>OΑX2-]ty0F;1C>~C}lG>y5~7~!%D4_! LT>J|V%4U=BOu[iwFmxz%uOiԖZ@4 7]h4ۿFgm8F~'jVu͗Y20q_&/#[1c|2r1i<2.k%_1yoĸ6ǧZbFO1.YG䏸JG\,G&ϗ5I#SHOf.ʝOm3mOeYE ;Yϸe_ׁCrb(y6Urrf>͗5f̶N&,sS(N^1̥ϱsȌ15r:\ ڑ(w lHpIHcƘgЎD>ю oۋMX.#vX.#m\N+X)^QXߟJib*>oU]*?͝Vx%\w;sV( /VGxm1.rΞ>YOt77]/ZF L_pD:2^:PŠwWj){F>eȽc-2t ;D2μAvfBٙYT,ː5O7az?geԷR8Cm<߾̐5+LD͐sed. Y..zW Poo=Q hra4!~;Zkn٧})~o- vg^ʙ$sL}'F|kGR9ۨA&}+dn)^y?\;Ҩ |4zʰ+Sz㈶nmhB]%4űgG=?vy=@:I-xg3Ҡ>8[m |U2Ua!}1h嬺PVf۱[dsgU->G;T~ea>Qh>e?o-jz6L{9qX6 ebׅbz/d;hFfqi711P)N\E >cgwV'QH-a<;"; x!K֐t s,??_-[rB ^Dy"1dȶ%."6mٶEy"A{} ߋv=w}")>f XLӽd\W\6bvlW>*W_-d5Y,cU?EsSHWDo3=gW^ɒv*Û<&c\6gy6@ ri7}a=|p\3}nGgIx 9E',x5KÖ(~t9zN̶lh78݊4+ 4g4kzKW\| f!Qw/@%r֫yzV%33&~~/*?DQVX*cPxZ<%;Ʉ ƆMy9+ذypć|X8W[儔U1YF;tiwa@zC}]&kԴ}sI˿?f6ȧ'e}2IgZQFlÞey2ȏ=kSc-f\l zOW}W1*\U h+> )uQ?*|AUaS)? Wef4g; w+>qAЅSoЀ' | ?s9}ty@t*hW.1Kn9`{ՒmpΘprű\b <]1/1p`9/ ؒkeu> d4gڷ>3˩vi91WH^Aw Lm6<xy+Wxu+?vjlǩMaܘMaܒMa4Ζ6yK4f Uv4Ne,-`ڃ(d>M2_&w-rH9fh˧PzYK94.mC!YC|C|C!Y!Y,$K\\y.)dK<,ߣ tOF&Wr?ДY>ܝOe9ܧrV ޸V6x&j@~5@N @ضøXgVɞ(_nVQ\%YNI؟G.J܏6Q4&fc*>Es2a("﯒+έl9{41խ}Dl^#H]qܟR7ry՞b<H{"zjuՔOV6iδ9^O,'eƬ)d[)jʋVS_ ZYm<В"ı^]r8OMy \h<;ڧ.%Ou5-]` q8/;4mih.Ǻ;` 3q svIݶ7yd/C0_PǕ^ɳP nk ?2c[ZeF7^f\fO ˌsZ> p<3lO.|{N| }wIOغ^ycoc U('@1;NdwlgECB(P,Мp- (L%{b.2s5*wbI_2i]z__n9*3\:&DkݻNl$+՝=b~`O'qujpοɹIqBE؋ᓤm]{2wVU0毣l,gоv!u˺b\ P:iO]e|{V o ? cxC6ƁX/+e (ZN@nۆQp_:zDp/v:'=t)8\*gtTx‹t{> G.s8c1.K6t4S^j裒w kV!}'52c}Ng>3b"Цg>a\a^-ƥeY`bWa p p0} [ xl 2ӓo|C8/mrVc}-z^goawwNĴ ȿVΓ! {?8PƱ0N uxu8g+-loBzж8 {ӣ)Cxk4"6(/d*.&[T.v/}T>v EH2> P4kGncy'1F{> h*H6\w)K}55?m1_cD; `l,*ۙ? ~k2i MAcwP[jTzt6MO Һ JR߬$kBYfc]k\T~pX쁮[ȶ eMSB9ctC0_(yI e|n'w|rT?3 *S}X7.l_*fEQҷ/n'>m&.ȧP/u5F_sGO?xWq;kqW*WIy塝;m N܎#]hs-oS:&J~X`S+2gW픳+ 9ךsv0 c=̏%2o1uME@{ywy5?fd?NhWy`1^=wQZ[@ySv[ 좲P73u{ٞn3zy8groDsgݲn^g|z =ݲF!wn:Y[֥s `7|{[֓x0{l ~nw== {dݳ-<63l{vz*>gSz974z|g|l`vo8^J[w7Ms6M+uIcvr앱}[*y@㩛,4S~.%3y9U /<^=>}CB'p$ !p.Ձ+ܗKb/;T2Yd<="GX#t19p Pc#/GeǙoŷb r\?}BWbZJIwb*3vgBk~81~^- %7 Pk2RF_uBB?߯;D*.ϖwV=O[/ESǮ˄z)8<_8沦F;k2}dnweMWgYm͝8;"鿙!"(pC&VDQK<@pPEYC!"x x "JrL 93A.zU=U3$|_ӯ_꫚9=xxN1ysNc` ,rf~?&IX?HJ^ ֹX_d<&Vq8&>X ?{)|n%Pe{HM[)XiX1Xz8[5kwgOCӬ8@=G^712h㻍agрRD͋Gbh8{;|GShJeOS.)ixڟ2/ւ)oӞ4v,TӞ 4ua z |/O h>kG,rHxǑq";%Ȝ}IK57^G?Ms44:yB1f BP~Dp#PCA_§*|1kި; >+p'>pD >l"%O(qbV8 }:U.:vٮ\.S k~73H~e"LyJY Cمq㕭0~]?ΖDtO:i6œC2BΡ@ۘwLٞlٍIs;e^וYfHJAG6(L$۠1lW$:$ nH"`vʦPXD4K"o2LHc$/N%}C_=l_xx_ OxȾxCG/xHVxȾlxȾp$}'uJ2W$}qc2d/;.[LVL~?;.g;L눼O;ʯɴֻQUp#jM;t{ǣGo3GiGɇrz>ptv;DÎO pGs~G}1q_p[kv}}4ܯ{%`z~pǹ)&hxn?ʴ9e.]W!b-GSHmH!9ږBrtceէH%94TY$G+RI6}JrJrTJrH#9H#9Fr4=4 i$GJ#9*J#9:Fy0 t$GIVNr7X:Qm:Q $G3|!{NSt\"=?#pMkCY,ː~ q,36 Y2~c ge ;q8Β3:xf:-n%<ɐ O:aR׾Qb~!<4@E8a2\=0!v;8I 17.,TvD ')3<1 d?c .$r`}ky/14A@We֗3eޝI냣g>pn9=?<y pú^4G,^ gӰC81'g @ely JxNY4& ,/ "{L3nŀ_{;Aa5Xf-֪zQp4?+tA ?Kld/jL< 1dUEQ&7219KXٲvW+-/d~7p8Sg JMZ "1+[t=x$(#OgSY :Bf٦[l=;h_ SQ/{0rӸT*qpݔL:0}y֋nb Md$<CGe[0 K8?~k$ɘϛ#{1F;tv(SAc~sDekrdMʘK9 rko92u|8qq;e vchA%8.`CA 8szGXCFϛ!"ʷ;^@6i&.~|1{WnC++nV47{2?e `} pE$2`lQγs=B.ep{$ϹY˪pg #q?%A c< k}{<<1{8sB"O<``'w =St; tsmɓ= y׌Q-oua-yґ/ſ|G `]O6%,{M92Gcɻ] /˜mwe}z R$ҷ>?Ovdg_%}˽d%}^_K~/wl/] ~/ ߧ>do(6(vh -QH1$R?p~!WR?Bn+|Ÿ_3rةMMmiGM-PwX=cfxᘄiJ~^anIk]B;?e@w8JoI grM#0~ 1mVK!Wq/Gki_}aE/LՍSemOT}`Zߝ5Alsu}X$yO/⽠m?+6)3QWF@V{\դTlV%SD?D5o L<&U~٪gҡKT /Uq" .Pثpy *pY g*pi *1nT +\pQZ*+Pاpe *\":sn7bT[&2l|7=c\)|ə΍Bُ[ՋW @ѹL,c=QceT_vQ}9T&~ʰl֘u)f83S}x"1G4F,5 ^ ܬљ|˾"iGkD콶>Ǐ#tX^ߧ87Y>o': q5,Lk4୨B,T|47Grz'O>)|ҧL4<Xc X~kܔ.,{Aef3<>Mt3ڻW~/ bw^!|We=82`mk~V ?JEgsUPUPo~r+䬖zFԧ+lJ%l ׈-Tئcon$R9Mg`IVOm̼0o_U)/xWxhMkUVU MPu7^[Eh֐n NU`9|_ت^Q*˯lK>} V/jwTtjҡUjWVM\5dVMe5ќ19FӃj\2FF>nǩ|Ԧqٞ텰!5xlFJrkm9JoL 7[A,|*:z?/VQX{52Yy.!ןbzs/c|_?SxǨ)݃q<pbqʋ)/A1n1y4({v0`S6u<7oy_ ؖ$7N΀BYqvW/L҆I!S~_ÿ4':Ds/}a#"ozTMcrbN7(9T/d#^~yOe3 w?4JES^oSs@d?ڇfqgsі<y;74a_AΈFaF避o{9&7^댱Bka ?eήgؔ>&Wa6zdƱAWoߛ~˿7:'(_[|Er>R|BJr!Ԙp>_*ocFQXobw_w6鷛'0͒; Ǽz~Zï4ݍd_)}d^1iq~J58TqZnW ?OٻPV&ѣ5Q;Ԧiggѭ"4zM77ޢ[_4bY=\jrvkĵqp3xNCs|g@3[~q̏7pC{xo3|OQgεxc9|m`Dpxض-L997(ni|t:nE֒Ζ=pB#a#Yp~Ef<2>Hh':ekG.\ji**1/lBj!Z ZHFW݄.cu(fk,봋(b2[t}3Pn\'Ǐ\Ss*T_3ZiTrZqZ 9-^>KLRql+J}4cwͅDsY+ьn%Zio/]J<ӓeyA{ paASp8N|eU6|IxIk2Ƹve4cdl"QdOEh|*=d4_qz2ltˏ2rI:{s}u^ q(91ݠưl^cY-֋1 F!q-Q6?Iy0EmKFo;\J^3!qmED~73/D[~~Zc5Mzn0MVl˧E}~LWreku~ۗ~]njS9G87?e۵.ĤjFv˚>lΟe.a2]OݕGU%7$`,Y;d *&*n(*#GTD@Y0OdOPFTDLA>tnչM}ǹuԩ{Nݳuy74g⺻c'M{/N7(ݼWv2zD 'ک!mTYuoqQJ YP~8Ι;WsWzznU>g@;_|c^coZk~Uf ehK2k dY/3vq8+P,0*~jG7YrGp* 77UZe?22С*3k\]j5_uZg1rn Qo q!q8ƿ|h;*J?4\HQx ^kg brM]>#hfWo3*ࡢMm2Ikߎ/Pj!~/ώ`zm\ 4F뼁ĸǜAϤ|j \M ,S1dBgM& c=j@5VfP\RXCqy5\ZCq 55$Ϋ954ǵv:^stfՒ-kI@-P֒>$ch-SK<ҚZZN-aZKk?nr^lP_Ka0ɸ!L&Yi0$0.ouԻ´0>uUaZ3쫣\u:Q[G/u=LzWm_Omu>׉ q,+Ã\j(x>m:^9gzI<15EYG㱅,-0䩆m2ެy~k=!H1c~>7DYB_Er.X0c~fu8GL [>oh]f+ +;<-~Jmaˍ&axCfpy ]X 9p]0ˋg k1v駀p.k @e [jUAC >6c$v\z-Qj].,=cnu? b_3ͥwpyP?u0`}~Dw:l狼/k}@e;g.^;91s[7(w,KaՐmOrshCdH{=G^N^mn:{(~ȵpueGgWߪڶώ簬,-wʉ4;1F vk7-}3Ff,)gt;G##$7A~DGk!Q3% }퇘u?dyH*ғ-ѵ}0ҧp1 Ƣ*NS*h&hu(h'Cx3 ,D~ \!fW Rؖޯcŭ8`o>y|θ([)h`=ռ# Wq)\CpsmxxBxexdP 82Еx*G6G}<3:x˶x}TgVPᚲ)Wٿ8xgRgm6}jDvB{|$#lH6H6D7߸dǙX>MQ?Os䱉<ƲDW>Ϻ,B?W|s[qo9˒7}.ֶdz걮BòЯ~bнbbLs'⸡P+qOi4x==po ͜j?d?ioa::4KP.\uB%WN@orƨ>NI' 9~4xʹ?&t ]K3lvld+J2]̜Pdli lS `=Pv+d H2xLȚdމ0.$x~ Jۦ{h,j~nX?t"ׇnMɳ[QϠl}b(Q^yePgnuR*+$L{SctCjp9;pv`o+lqΤt?%JEQ=us/.NGyODuψv)F;~[ӷRNO4U޻h_*gk9W7Q~|a<7|s}Orlb?"NZY"}T}9J K6d>lRf$+\w~k 2}6i^G Xg4Œsw'[Qϝ9r{w5rzjxb>?1@sO78mrCm~K \- jYȲuH<LiVӸ\jF嬁q =˄傿z+bSϽx i_: +͔W>;IiMq+si9;O>wgZ匟nAߺiNZem^o8Ll_ajӨl{[X`X!"V{lg^{YGBǧZ]̫|*axה4nך+Ȼ[ϙggU#cVՍ&_)q!umzܚsZP/sz^l;x `{f1hg=^8h(7/va5[ɔhfƘouȴ92i|I#dF==5 <]g~Z&T 61؃1Ӹ);3s|΋z>^AE$9gl-yq1k=qz%FOnsϡu tkO{<9% fW./uKub6iڱ\_ɸ~QASG2VA5?~^^;glȇKlUT[w GK%q=d8ϫ49ovP!7?z{.y(OW,b~?""}V?4}G^"GH9cymV+-{C t.:z_/:ri˴[f_:?P+ݢB[խڮt=e}f^%fP[?=(3{B'(OZ->*e14+?rnuVa:^g٭,6RwXncqXFm uKycֿrG"ٷ Es-Ef揲tY,, .K FY}je?ʒ&њ\dkFxS}y6(fɳasi~H;ptT)Mtfp{эFz.ǿ=tf-GA|yy]92;7hlچۃf?n ;;ӻm=}Kz_>\ꋞ>υ8#*z{4H E챨y'G]ǁ?"CeE__mdCr_f!w)hk2\o7|mz,aYDFP(1ﳭ_%;*0\TI)$t@B"$P0B RT(Jb+0( bAQP4*좀o왹7pCD:?o(y߬B} rneށ8 m*Kql^$w}ӿ{ EJk"㤏vv&g4^%&XyL[s)|KF.QX"(|k^ ߤ ߠ Rx+^bwVsMXu4a4mO@9uGe|Gq4~?/mi/{k>><֔Şlv+{XhVB_۴^wARqӱCA~A= Ax6Q]jAqȷ%2v)e&xK`$xSLt#9NS]GmW:u>63YE OےGϜv`uq-Xmv j~/T82Gssakx%o{cOHTEJj G)|~Ƴ=!N5qqT#Af ϟg=Xnz(=e(as1 ޵ĸ ~JI# 8if'P'Y |FA6&q'ћ(.H$^ʼn+#x$yDuw:\#{674h&ji47iZ0M֡rr |4Qr!{^eDw=ֽ0w#{gqdi#eG-)}>N3-uAhO2Q%O2wO]ڑ m's?jc֪~9ڴ3+:2/NlK9ٌq4V8c~,FqbLls~;ErVޛ/, 8}Rr_rr?֎i>8on'kt?;m-l,e>څ?sҧ*z޵֕uxnqZ>&M \=T.G>q߅[.o w %GJF+Ǚg̣AHi>(yҭaZ=Lrf^};H?.徬?TNuH=/s2'g':Fol_Σ v6131SA#0n+oN<ykg*ټ *(ZIO20]f™ 7 1  9T7VumtqON Hz㕜q!2m`|M0)p{)p;+I!qbu%0yiABxu52^y _δ' al7lM'|Fi7 ^ |w#T*Joc`:R;#ϧw$_=ёΫ;+NXeF{6'\F܊<;QVxdznQE笾h\gw84S @;Qو§?c?p=|[סǰw¬N2^7 /wra*G}591}3o~ٿ ~w1.Ì`A[)͉>iy~2RGO[-O;,O.р2F/ R$q7}«^ ߠ·TS^-R4F ߩW9u5O;?c}yw A_ȸ}MeSÎ{ܵ]^ۤsvSŰRUXzAie\q9}_ 燦?氧vElOhoo[p,mgs4LaMu89||t9|v KCќ18tL.BgƤq>b|m:/t _!|J𾠐^EiDaOWq g)H q^\~<ߋ0-\!d9MՎmd ;6y|+;*Ygǹg[/snK9[roC8'L^HӃQ=8$qckNGfep>Et9)/~W2. k["R^0cgRO ˌuiP+d0F'6^W.X7{Q\{Iߺ pgֵЦ:»-\ԙq}ivo8>xh>i˟* ۙ~4WTZS=]"T S׍`:}Tk^e=kǍgE] ^NFuޛCp7Uyt ߤe /Q*.tp^>[\RTyg5L۽; ۍyF&[1lY[ /-Vmݽ)oK%@h_SqS3˿N߬pں.G򯍡X/=F;$HX8yLSub*?~szΌoUz7(^ERa3 *\ydh^f!8׳|7[\|7\A}GLCwxuc6J7>bhl:2xK*a?Yha:@#J7%~#L6=%SrN[ JO?ds@Cy5zG@sc?❪ ^?eg#M^D#V8)-^?.c~iNc|>8@p(]ktSP{t9bdگC`/YUV)쫍{~_F=hC C^ "h; 6?mZmX(2hA ͱ3(7iL9.t{a(EZFu)]d̄ ?I7.erFsX_/|0lݷ<r#4ie@ȫ#e*Uaʍq ӧ*:ۼC6bT8z8#([6Bõ-SV~O"}~?|h8MU"otBGoaJ6*)úg~i@H+~9~7cF31~>̈>~'Wm񱼷;Pc*/cTZūmMhUm R'C>κݎ_Ԓ9À~Cͺzƌ>i v+nнhHSl d{:uLe2~μxn1Lj;e|%~X&y4ͤ2e?*Sڮ?3Ve^ʼnb3>WdklBQC=4p^,[Ԕ 98o`/ncFA2q&}T&/?Bb 9yd~ s[ssbmVXe*|E j*A2îy߁v-N/d-?~~9]3L9G0K9KKߟ}stL1%g0 Sp`jLo6,j6cT;o}3=^[ vH16U}<-#c kX +hJTz^89`vͳ͊_]U"qZuU忔y~9?8D쾘6(H/ IA\2~{sCgN1|8k8yhókJU\dW*|K po3(wx@=l;k-1e^ |&~IIaQc~)R;8~ 5cK 5 >[}*W٬<ڪyEP&nyv1ZE.LT?tQ??/]Ȣo6C i Yй.6o,(<טcC,E'TXoqo3[<Ƴ'DHYmu+mSO[=K˳*1$,O|6F,uSC{?cƘ&qoϒMFեv8CsB%Ҭ9guL|hݑ!ver:<ʿ;ЃEulR4c O-Q¥q~vSG|R9ac 071#=޸}y5w3fŜ?0V3ĥ'0{3 A4 ؿp)Ex^KcŅ<x)os.c9?\t>m @,Aߪî}ls÷MkMɒ5o8dc<[91ٗ#;FW#V2LˠyK\Gh'VS}"ȶ-CTG}T7BS)G#4,M}&é~9eӼCX9&3N슕>"6G𵍩xכ1.?4cl'619\;la^;֧cp~Nwn.4NƎ۳ya+c&\bgZ@^ ˖nn 3|g!Ҍ8U>UchL^3 9c< p=@9Ac4hMcl{ʦrH6$gS1#۱˲۬ɦ8ß;iʍcM6d˺Do&Yv g٬?0M͑D0h$y FR9Y6Rքlcpc^m@4Fk{KBJEkyFyYx&}HYV0׸ّ|qwcH𳑔/9_Nk0mQQg(<7knu\yq̧[%eh h hnB)CSwmcws*gk{/m@׌o~6]=ʨ {˺o4a'u#,/˃2WCcdp܊1ʜhU\P1¶J2ƈ` yǝ(Lƈ޶e:L%S~cu^cqD_\7?`91mM8VG@Y#k~3,kW7Κc ߷faԫ0Fԓ# sN{u7'}/? q?Zm+7-9Ծ >s0>a&ПtvK9s4i˕v6/ Ss͑Ÿȑzu?g~t#;&?ћ 34in?(,Jݻ1?rcޅ/6yFpzcA)NxqcXYAi?6z!ce豲wXryFo]dl;g ګ>L6=_(S柱r.yq҇&4Qr?sǝqEE?d!Q UnƮ.}dHMl}"}S~+[Ĺx16"'`׽jm}|c}*UO3}{K_k Gd{0)w~G6;'ParHoD2`piSSb\]"<<<}Dq0d(8óvTgYs"9|o T;Nzc>gMz?lM^`*gYRM`ʛ 8 bKzc9~@rCC4sӟ,r놜q*@&XD{ˉVV/;G3O"ƽb %c`cWCVՠ} ||#Nq [e.pu*MW⻔{?%CpD+eOe<5''JٍʧpS=n5PڗL؝S}B=ϵZ81p6% y|'QIǛ*AjoHol| N~gձ˗uv6x]W6_l9M_4NrToOJOSc '|'Q8˷:A$ c$ #o1gq$ IƆIƋ]=;N0s&_d5d.L2O&oL2줋4vM=Nv1)~P^4l<\)tV>wW><_\9XNX_s](RuO!b\)M.l ޘgctm)׌ (xTn<'X]RT8}*d2eU6vSo0B~T/idX zSݿv*[Ryx*爧yc*O54J)oM\X@H;`S?ś.qUi^rw_ B#WF 1o·.& ӸM&z -웰_I5Ծ: G96L!}װ~ ψ=͉ JoBWk u|ы~q?#}kq`+ϐGڀ#J'і}G1=H)o9x|4ـyI/y@!ӂ-[Qw@zU@^@mZhsq0F{~wJ? ֘Nut7ʄ~;mhF}IS?>]lO[˾I* k~*Y^Qx0s7ޥhMڏ*IM'L&LM/{&\d=musϧ[p߿RmäAN[Vc?~nU·FeȞ~f ц No?pqkǐv-M9'Fy8YA{Xل 3]u30HeeFX zfY M@3DoɜA%` j3gϠ6s j3oAҺ] @:>%rUy?TZ?˒Q2mx (6 #OxiV$œxdygt% 9?v&w.~ԗ!cY̚} &2o'} .a /ރf8g&9Ǵ6f:X 82] ? 8q*e\xRu>Ƭ54f< x:` xbA}egI醛&wpzh7rEza<ݲ#e.?3EG]̪W,3f cyzwDͲd~w$Y ary8iX4n 9}>qbcZ z࿆u_ZU~mKOnB/YӤ?SOx\;B.;&+0`g.=7u{7:g>_mϣ!ҁhy~`9G羾9+mn~<$GSX,aV&.4[f<8~  nGp7 /tt'bps5W *$ 'I]oRm.2Q ܿGP[ >p-;}MY i/̅.7\/v+z!'CY7ð\b-n6O/"}pG"k\7c23h1:ߕi:p -GyӺn+f"$p;w jϹ| Nr p;x%lpW\LOp;X/ [y=X}Kpc^pA%3k ].QG-%XO6fB~s"H΄xlMXWfJהNakjS7~lWbC"kܱN4&7K%NhW$c$l;m_juh#F*aE43iaM*"ORB_ϥH_]$k_8⪫nfma(OoH5HEO؋.6㴷izoDJLӵ CWXN|+'Pe% JHdǴ׻\xY{(> K C WWtDTxK?vdv1F}Kwq]T^\$zߔVN1͋ gb.kb߰ئ\f=Ci\,oCkmJ;ɸ. yۂqkY[7c5#cq@u|n>* 㻩rDl'˗*uiv<p2Bcπf?狦X݇,N2ICGY_(J`ZY}=4 A;g4,{[=&VCZߓWɌNa.SNc%Bx({܇w39ٌΑ_q~j+2>[m#r-?>$DsehMMsۏsOymvðxˆ5V` 'Kd_z{&k%~wb 31nO= Ե sBG"ǛdnpFj]Z#?BGؕGY$o&D0O9<1s} aA]D!'!(,drBB0'""DWTD>UoE@U}UU0_^O4x&vyg|R?q?gDg}$Ognԟ7rѣ>d[rс=xˏ&~ek^ixc7=86e<|>DS>phoz'_51t}>ۚg)K3]x;Ly*S:ɎIH7ѓgyU=\.;bȷhyr[# '1ݒP9QHsxLu'9 gS>;msn֫3|@?.Wzg}˸6>刬Iaq́e ~lI=}tiyd1bn ?}r Go(?1O:FN+SZ8LX}폌fxAxYʣ<#\ }(޻lGq E~7 y|{Ey||~V0NǑ/Yg9,;ϧ!M/a;g#s6<‘خZKckwy(m-~M0΅ˮuI[$;tFx ӕ؈}4ئiߞzZ\nkm1*\Hj㭎O~{|=ezȤ|+'9Ҕ>$v~'_O c,'xgo8 '<~yq;:k{zp5ONMt.)zT<3v ؕoL%Y->Yۋ= h 8@}_J= hĵ t/A825r̤q/?"Sf"kC&?ORI!݉ K?>FN _aHt;_1gX@YW@4|h|S@4VH;} I_>Uq8kOe\z?}5g~|#s^`B>v>xn[Ru̘TH<ִ ~pƺ~fvaۖq~"B8ko7_72Fʿ6j7ِvA_SVL: se}K&;J7ǢM$Xu?t"<vCӋg0یb;㧸XK/28_8?/&P_4).Vۊ}M}_Gzȉ\ m,y]SvźX}tGSӔ~.G<?Y`~&ܭپTz Kt!/ӏ/wwĐA)[hH;Kߌ/~3qd/6x|f/K RJl-~Tײ7]/6@'Tϵ>x=!YJk7c A>g@]]I_9~ _ c擽'W4 :,M6}WkvUOgj|Fe7Ckv } 5Os_@O{W4 U]ﴨt O*B1{9Ns`D9&`f9ѕ))56NN%Omo3z řqf.ѳ=.|jP>뗨&qvr8Nq Sasǹ4Le\LSYT)aCs´^p ô^G´^T ]r[twô^i _zak[k-Z/ZKfHﻔd2EP@R?5-U'&sqҖޱKɧu?|/wxn ?w>cllxퟠ'($3j_2n S_(3xq ɏG |yd*\>2s)0cVr{Λ~O2Uh zɆA&k62f (]hitCސ V݆x`PTe ]y`>'ߚH4 -y>l |nڂv,lOt0Ԕ\C7 #] Cq|;;ϯ%RWxkLO-Yhfhnh;v\CalZkm٤95?~sHy핝v]u}Cg_S\bcT Cu5# vƨ QWC9X|v槀7Mu>p#r,3c5c 7ƾzVzᇌQ/|;n<--(ȣ.b639< p2s/+ xG';9{,yY7d-1,jױtջ.n8*͘ypix:@翸<x||qޑ̇}˹-'.gAIπ,v|6cp2 nUX sꘟ0F]i.giX~x.t( GxFLυ0lлBw0P0装|@a=2Vgd:@t= D'x1ӤmUrF;x*V>|}4J8ğڀwf;ޙպ vo%,OvόetrM: ɉJ'+錯ex=??wԲmpJcޘW(ƴB󸾝koZw,tW68΍,sOԵgkuZ]{VCS{U^GynƇޑtVv:>ܯz5Cr~kpjCm07Nu@ P^qN=wjn _a{IӦǭuT;>GMyTG_i(N8ι_CE[[F,0KuXuNup'o}=}{gjC>>|U}FY/3;1~"{ ]֊Y1#ޗ[|?tsrsZ֣Ġ]v/Ȋz]{6J^2u`&gn6.+z } i?kS;Y~I{E?m=E[MOL0n?yh> xy7eR'9M;'||!iJ{M_~ʴ{kcďUuYOC4\k}E1_}g`XȡbgnWu6>5N&7LgC4@* dȎU7OdOk$wk#4+i HvwɎΆ8ΆHi!F7Mt6&6l5"|' ckS&2~3X6ۣۯ;{E3߭լ:25m5gdΉm݌-S~# JQ'ȣY>Pz}]vG&]x쿕* yb,{Ȼf/_f_sGޯ)`{\}:{Uw=ߡ uGCHf Oͪ!uh;iVE@99\/ˑߞ-'iFue-#:e Ңg6h!yEݥjP冴% eH?r]BSu^!_D?tD.a@Uh0?Q[(8WⰍo&o{Klmqꗍz[.D{Ym?Q!c=AxR.RT^IM6]3glgx 6:$6qJw4q]tά[8qHFc_My a}۵m>;]z1V'~1'}:>}?ZEKuԮ=ݮ-RNn vP0_r]}Ntzwų}MC֋Kйo`}AecqIds4YCF¶C}N^7'Rͮ ٍ{X4;^ G(oⶼm g3nx50}K8G?Ʈ>"3@(PeXN5@\OQXoDA%$r( [L/7UWWwWWWwW-no-q}m˘!72uNԃo@aln({b%&'Dz%bl>e +_cuLL!Ro'20kX{XG͙g|Pf({DDzywic->}ȕ9Q>f8';Rf(n{ ض>Qn%awY}v^HXOS >}6JN?+}P|NUW{IzWk2D9N;I;.y{BӯxB4EOkMxf9AB33up^Ek]+x߫3qhƩ${ƃZ3ƶ/J x><fޔ^x5ϒgڷC;_q;G'lG<49|o_'M7Aog<g&&nu&_ ?)ZQ''fd #Asb2hNKYɠy_ŰDh,d#B(Hm2|2;F->@ |2JOdƦ'3)>S%gY>O G3r.O9p#L5>O1a j|Wj;m'g^pPTi{QvR*^&륛RyVIE]66m3=m"mTRی38%qJsJqJpJdqJ|dsisN;;ig;榎aW"i}Hi5w}G ,sص@< rDƐCx|luK DG~O40wn{Am|}s̙>ٷߺi:G<Y6k)>>& 0g|O2l;q&yqfyDy:ժ\_lMǜ^x¹[h23;];YN8JSSkTѸKJs!Yɶ R&UP}Jz񨗇]ig}&ۣ׆5N]|S.c Z=k?cr[慨ZoS6A>keϵ:j}h_s?3s]'\=[?$,>:LcXC61g[6b{ v(i^{ߛ</~vZI߿I{>re֖(WW*eo׵'e>Uθ怓%A?ϵw#x[c~yW-:ns-z GO6gl+<;ϳ12l*%8W$=>~!'_b {sUtFSA" [qsV~9[5sz`~Q(M!ʟml=fܽ=b4ͷ>bv:7#g}#$ JOͷq ϝx- t  eH ~G>_bzsA%6&ߊ Xs|!=/KmWWf )SvvC" v &Azׄev\Yjާ VNk6;oyy}{V;^mt^4}1@/ ܶYhvbfC2:i(mωr.=Az2Xs}7.xzoC=?@g:Pf{ \GpB迟Nx0τص} cnS1Ӻ} ;VX.6_/|4|U>d߅:uiP{B (tw8iҮD@|(w~!ۣG%6bѹ݋hVڵB̫Qhp ]wl{<˩ڨl :)˜־T.E}S}h:tA;|_,7_?T3kwE(0Uc|)ZNGT绡S~ChNt涧%"AGampBamp&LhD>NqNJNN@ DS"P%"gErΡ1>综>ߵn.Ϸ>߆b|w%Fc ֹCSA_P//Swf ~ohl?0#31#zs}林=_Xxwmg疀\rK|RUu9ޱB͐"eϗ׃d@il{v?Pbfe ЧN)韥)q 'e9e샶̖61GbI&glMfԨ5ύvyX9SC|B"М,.4l;[PBwĻVכ k#x+8J'+8Z)JW{~3qO^KEu{Z;A>F|]GX= ?'Ǎ ch VY3o\9fw7wr~3gv蝷G/vv(>dΈ|%}vu*FlNnQzGrl_jenw!Ȃ .ڳW˼Xn9kگ4<*.} >w W㩥ֆ˝9ƲJ-x+mGys¹ϸߊ5}wOYwq?x, ϒ6\޽IJ>MωӳUerחurYwG+-C B'XM9).C{(ӂ|e ᄇk{$>PIn^rխy ,G^#ʑrb9zy֮-u9l尵;V> +`kXpNU kkUVgVLqJ؊*tY%Xb+q^lI%b$rs}dg+Kڔp*k'&[D!جf ۔/Wʹ'rDQ-WB*!Jȿ򏪂{WAc ; UUF_W[!3! !ՐB5?eXR GjS$jRm_~5lOo^;N 6W|r[-{ؗ6Z4g:w߫#0dbyOވr~:jewt0}(=Fiң۪9zM]kIUJ>s; W=X5gS fh $_һC[FL`^M7tj$ 'K:L5Ëڀ{-}l#<.Oʘ- * y35gvn&/׌R6Ysk{| _׀uyV<@]|?kx0~Rd7u "# vc#ZF褡v|6#{ӎُܞ)?eORҡ,'JQᔦ퀽\IJwSߔ] O?fG(o("IJ\n\$ tӿ(iJE*tu5(=P253f&+xZtI7t޻Qubf0Opzx(ieiҤ~''I84 \ NL:qozul|g?9xf rc7yhPo|w@clD^FN źy!^M~#|{?y!J%o4KZ}C)`ʷ^gO8,1r1#(&@i~笙.x<7n|9bh7fИ}8p!n8pCl;y>D2D/ੌcc<l7p<"f<&W x@$KhpX6ntAyE+[BqkuGv>+D&[1^(` lw<OgIW'mGEz.Cgn|q< I[~>=bLuYE}V?c}`#곮|%+}-t|#Gi3V^sq'WN}Lx$0:k+pXʦ%;#l/*PS%G䬰#߱Vkʤ-c|-kۣq {%=gǀٻ}4qktbt9mx6681)lX2Nʙchgx%/PA3hϧs^.%e;-*Mtj~L!+ڇ:/n/O@:^!sx>^T܋y眱3dC2)nNg~>fgP;-Ə^Vx(Cg>kwZR;ȹj/dma%T*o2mF$͓rcM{gxOWclt->S'l\9rm@&eJyz832EkkdIyb̽H#c2xɒ;/>26[<+  @f9妝-x)˶O[gW[xzYwMGzw+cԻ;D3czq`o߹M6ϱ{㞬fs'2Ƅ˦~"I̦~I0 I3EmųNvz+ ?m=`Пql̞w8[yqZS<9_#rLYj#SN= L]Ӻ|Xv|:.73݀9$G59;08pπUxq]n&*xtEbU,"mdpħ921w> 1#s 㸬s.߯ 7cg0ccZIn㱀e<pɀr=Z.@^>vz>WO'PeO*K_Js.ȿͳm h rzδ$,#!7/ z[9=*X&bps"k?9@3WW2k H:z]vS`1)"h'ȼ r>3G~>ݚ cL| X7*Yccd}P ez@ތ4=25Ѳn}k\H=ܤ LOǙ|yEg//}]XTvFկgw-L} sFLB'=( ݐ1N($s Bgժ/9۬ ] y{Y݅2l[Vs >#F^DmqTpoG+|‹R>@_ ;[}H%wOO5Gz^Z$1EƩ!^1]~ߔ'\b. 8+~Og7v=:F~╟\d~y:D2 8s\/Y ݶ<!!rP?f^0TSi6z%ll/M9l<3?dTyGtLk1|Hd=fƑYxF<;IZ1e"ٷ1*eN3r{1EXSw%|^Okf|u/N_q/U<5m شk>ITUR]&YC<ޙ_D4]PԔ9%><,Q_ )HSR.떔\68YTW+|@8_ 3ia7㮏hTBq:\ܥVP) xJ- ϙ!⿗0{!&Zx Z,ӂb*Ud3m]Bs=JEƈQ=5W[R",X.Wfb+,oA^)%[1}KHROoyӕb^B8*Eu%|xO6O"ەyB&fqpNiEB7h"p;'!3LgguR/lsysz- ݽ<|3Lҽ#ps_E Uxern.d5q}~i_udcU.s27z6gEE>7|NC>}?_ǹNVyagzݯ k>ø-ZG7Dõkz,c#Wp0O8q*Gaޡt云oL?Ac_džg6&71pLb^NS8wp4PL8?yb(6PUf^wq8SU8 \"y, ~:g(yf6P.!w{pF?߫a%/W=Hyy]t&8psPz]|!<5%$p UӰFLo}X[Rܮ/kFҿ?m$:4Hۏ̫٤܈;IZ qVƆkuegɎoMi 3⬌hp?jrG'K;Ipυde_z.LJoYmT;T:}T(v[gegfOK({6Y67cOV?F;6m>5GYncDQrj5t^ gJ/L=-s"vD}Yw x.J=*=Wx3`Y*e#>f3B9ﮔu`ٔ*|"̜`\~%>);8ZMV4):ײ*m]_wT^3Lh{ͺ@8i\.ͼ;_<4>8=ʨSԼaV1EȃqQ65~P8]C ) }R-KΝj5t\L(3(0.8iU0x=8~cf(: 2m+K@6ء㬱T%煮5d_` B;>NA:T.܁21'lJ;}=x7jޯt29el-/Mp|Aީn H^MCqOZ4#ռQM"(@j968}QgVTwqNV/߯w4y:K1 `OժBrzՈ^ɦ3FG٣k<˚\#k {Xyj6?sאpZeIkL<]#vv|ypo o7jd#} ,s Y?1ֱscEo|@sכw%;5VƎwdyk/ߴVPGJ]oQ+uU?LM-ӻ`TK:zxw7d3v H{<=hFq9ZOZ9׳Fֱ;р{}5^i1>Y~? x`G{$|Ol %UGr86G:íx2jقg/@ejL62n_t{yˮ ,;j:ȕerX'土¿i68UoӆuҮvxG5mፉH1ܝ}&O _ {4Иs50]M<'W-8b:4dQ{Fdz9|W䴷b)"4MLhi5xos=/+MɶarF*p7hk v%QI(rtmUSm u0O1.Ɗ^_xjO-~PEt_t.Йq۴@T4:N o7oZeSb_~vnn"]um1! 3ݖ3Jbf2.%FۭQi!?6JK|*ȴnwK̋1r} >v,icyi_G=@nGJ7­r>^;ވ~+-C_/LZhܾ%tN7߿ l¿km+8͍lݘNON/V3DNۃi.aoK[U" ߟ 1no_z,褝Ke"o)Tzf?szCEzÚɳ*\c)ݫEõ<AVEMѭ8k}ocNyPr ='GT N6.LR[ JwFjC^*O8}&nÏߣSڜdgDZGeb9.Wzgq4Gs3q4IGA(B?X ҕ,8)a$[f̶v̎Q{!nBcSI2Et1]獖TÌB9[nBUi+Gp\>=żTQj/ms?"Qg 3HcHDu3ȋ)vG2 :}ƵIg~KǾw-,y974[9f92ˑ%\[3<3匙1q&t3cf/3Tki_y-N}" 9of\X*fEvL'>GtCji,NŬt=Nswi-4GiʠeN/gPa9;@~a9y@{ĸyќiAAA[A{26eP2(3(L_L_LLL?ߓIy4~> [qag =M?9"lǹ@':6RLc#kqdS0&O.Sxx.N_=+u)a}}ZZ]KǷn߈qw;t??K{o^氨 6Yp1dQ=FYTnʢz,giҋ =xVnO߈)gx=,p5_tk9tR {_EgH=|,n#3~r=!dC3vL8Kׄm7pqNl=/|צ*z^LDu\jll*ITgʶ`{ߎu@>20kSn=Yqlk=cp?ڠ;20"A XnM Mt_sZY^,F,/EN ҊI/!u ۩o1?Y= R=\pz'jz?tA)~҂Zse}e-^?0u.[yAzڦrJ׹no55yx{J?POv3V|+ǧiM?%)g.@׹WA<q!X\ٯYntXveU.͍?F0}isľLuI,"L+:Ye;m6xo\^SԴJnK@+րN,m,#K²hGT<ҥN=$^3icI?O''|g>)~ȧg`^/, ^ ׻ ׅE!ZPH ޅ5BBua!J!~!e!z,h>Gkq|7Gv[}7Gkq/{Gkq_h-E^_Dce.)"]aDwZ"Z[ n+McK<\D6mWe˙ .#ZL{oUGUKW)1 Yp{.k!NBQ皕XylnYB+<.2XӝJ^0W1x1:0Dqi=̴f]n!{}N >2FŠ-恱ѼO_~i&o =}Q ΢%DG~BDwf:i~rG,8?7ZΚ]z516q~WPHgCQew*I;coE9Lx7._Rq2#h*. _SsjBږގsas~U9}2V -ru#5ǹLYnG>=BS=<rgN7D.$oĈ6?>ϹW<fR> oJ{Oc ^ccs]@1yqAɯ7ÂrEC$k&@܋X2iN[#_W1?-g-^AmVezqD5>yy-HygB~_9W]I^wCF=(wWPU|n6fΨ]!nn<3=mg] 9l+ƃL3zC,硁=wHg)}D:C p?/<6m1~xLu](\_ kes8<kxNWf p/HYDV !i0᜿[ʋ3³\y9EHNH sdr9ZOMԱu!of~afR=U|b=A0Uk;j^sK֜|)v ܏~LF:9&+F`N2XF=kߐַWû{oЛ5vnCDry:EiPg;UfSkfmcyMf!Ox H \+5cs?C]F\"3 ff)K͘ffÔ&.HHRX$%R)y>s>ϳ}{Ϲ<9ysCev%Dx#)s>UB?^l~g:B7_=27%pZXj޺^Í?#B'4]F蠀,USs_9 ]RqLkT׻<߷ s9P1Xq*x 1';*z?3īNW{Q۳΅iyߡA>]-7T_X= ߃R?{Z x|_)ŲD%o䬕'—!ږցx.td[ڃsbgD߻B+Y(@hŔˀtPe2Yxq[:c0  WD> V: " 1R"]8աwz[Ŋk΀3ƓG8\HV,cmv /6s*Χ8m"׫<תy (Xuڿ~o. RJ*V_y,CwnUZ_w5k':[D;7Y6\p~Q^b;tLylE^;Fz׫v _a4gFsf03H36̣XDo(sa2m赲 @os:]*O).rw Ve{~;&ssUlOukОޞU~X\?"K/Ve^ӦLnO ǑSzQ ky}q23{Uh{@.f{+<^_0.c,wn}H_>O"zNNA/y}|KwPSGT v|)|#;L}VR{MT {Tݿ>ilp{Ev :%I8‰Q'_8dCd^=i!o8u-{/&O*rt g1%ƿ. b^<.<.SZJ*͹eJT^'>u nz_ӷ+w$ޒ aGA6cI6$tp=dzt$:}u9{kTV/NF^Ia)׿_ W*J^,;JTZWm3m_qm0:niwm"3cu8g18 p"wd)q :˺J{wگ Ŝ}(_s^5T^5o MJKb+Zj?3KQWH NgYh}Y$J^mWgw=U ߦp]oUe)r&X>& {)|[G:Qf=r.lOSc̆T>9c_$>įSs0ulu on>>O:_9GgtV˸lXժ.&mk.ѳh?:F }W r[u {%m|5cp.kP6]mwrXOT?I9#/vOmm]t@YT"}Q Р˴Eq||o.n Ƹi|> ]VHLk|s??MjMAmKaWъqv]oZ77bɌK7[0ؘЕb0q,S/ L/1eAg>.t;`;ᙝߔw[]`W ƞP.*WҕM,ߺxi\ܐ@ `cƗD9 GF7?E|*c?ݘ2h7<,0~>Wi~ /ն~ej30USg<6y3X溌 wce7AHOӌy`n|78NQaIbԹсиK4XcAfnBcja7>b.Cl;s3,'xԍFr'g͙WlC1E͜UZWz̴DtAca;M;?),è^W._񖒟@ETqnat=g85^KǺs8?Ӣ,>1dkDV.g?\6V*۶YcZ?p V`.\]tre> =l.',p{]OH3N0k^Yn-ط: R)^*q߽Ԟ=cV|Kc?VD 6TwNyacUO?T'&2HױLwqlw߇>}z+J&w~+:闸MZ :F>c;n@ף5I,yI7p;<؃=_q8XtU7}mLL#˹}?r {J6mؚNw9c|5 yEr Ǘp DǝssIqS3cBXΕ=pIO{:K D弫Q/yW^^"%t>3L]cnc{iqL`%t>yf/YS|D]A{X8̻E4랍\^7`CϯjLnT;֫^Ix"iQ?t"Iq7)gF$H=OFcC$"gáH#gïE}_+|6E>B" Qd06|6dFQ{VDφg{QdEl)|6\" գgCh&Z E$c]{GNdyhDS[>g$-_f|hگ<M_G˳ԿG 1tmиC4bcȗFR C2# eY K 1/b_PC_ KXXꗦ/b_c_Rc_RdR,~y!e{,G/gc_~ݛYo_o[P"d}ۡ1)k)vΘԖ'0ƹts|*? \\xskdqGo9 5z=z{LhEAYKȄMk^U8.oE.gwa_#G}(s&!̅w1˞c>}Gy ˚2G4xpp,O\O@O8ۚDbI#@mQP_C%%'C}H/%[#7X$[?( Lfk¬o5O Q&6֫1?هdAgEWptH,V=6a92 $n^ҿQ&O=oQ'^~10'c@Kv'o1>o}hN@ }Ywcd sYo`Z 8<p;K1-8o⻀':}Iξ!&A[~F_ b_< 18 TAe1`0|>G ![0&#}Om7\lZ\0>OKo3^Gx _=X6z*|;߃a' 1hOpe_.Ë6p.88ut@+VN@BVVi8cﻑ1Ƹ0G.cXwf~"уqvuZ:p.yڵ8YcWqjƺط&z䙍hOmnE۳pi#L0;==:ܯ/lSUh4Gy-jUorx1&>]vCj.u:de~~u벋ٹ~7Y<6I_cZi[.q϶R,2ƾN/VSv3!ިvrƴhe81Qgp+'YGI-2+^ 3^ҷo:Aw1Jb]oo41i7-Ƭ'|џ"@~x',_lU<hc*3z& tJ}O"}Ju( <%}\%tg+pQu878ÿzOִQ5^G@L:OZ$4udxZi?qi7):2+c iUOz+^S]ؑxх}/>#~O@;i+{kY|O B9GmL|+Tmt/fQ~Mڏ}z.\/Rq9LZnvI$,,f?r**]w\wSNSn~:߻B4$RŸp3 5q>zc#3M(;922 Nt||Ϫ~,RxaLF%^3Nsly=m}1Sg:[{)C7Ws1 6m!A6v0ci^&F~ܝqVXOW,9G96.X6.%v@z[j=`r1}SHfn/ _:kȑ0&Fv|-w)C9\7} sG;Sj甁bdt/ 'o`88/b-(:abHuI$2ƨXاYewʖ;at˃t0in P8QTЏ]B}<gZe>G22yKTex񁢧^f{/B޿Q+8Dwa>c>s3Uc_Z'z:v51,8m6+8?S|_<8ya ?x.5o*x|H9ʙUH^C#U}>Q񏏿zxG`Ky\fO똪w_aeIo ރL>Ktl1|D3@2ǼGHgt}pf&> שgt4cC%8=`̼=Q|HkS|H*,<t*ǽrDt>xk "t|=!d£G0S gd .KT5䞎m'ֽq}4tlX߯9ȯGs컫D_n%I_TM=IiOcw_vg\]ӊ>M[}5,M?W86սnnOĻ^_ˑ6J¿SI'&$#IbC6-IlDx赞MrZ֋?oB(.|B !טPbYGS/Utcdsqtg禸r]8H]_ez*]Uw\p*TFe`;cr_ؙ7!pgʡ q:L,d拀뷙<*u8x{%`> %trsv/{dQ_`ѧ5|U6, ,k-T><OFoU;N_-;tĖkM^1=L1iXumP#c cFr闪dY#&O5VkJ;1m'puI߅^D"6=ƯLmv SZ֭[&- $~}b!4 d"C  [}c4vKA20ʊd½ic:r)]L%}%]Ѯd9[q YVa,Ǔ=>B21Wꤐ$䕮)$ J!yeB ٕ䧐]3)Ļ]+9Bv%RHW{S !}"75a;lMr̐,u'Cc6n1M圶 v+lO>ib #Dw 3oRjim-U[dο'ŏI`:RwC!Rjʸ:qMwXrG%A*WI|[~#,Ʀm%8r9 $U͊7S7Uh[S=MF4 =],B;g\k۰5~c+k}!e|ܦàcplF,N2ƾ1^7c*iGs&;ukWd =8PGeT(#i֛0xh[Ϫo/Ź$!s瞖;8e?!f h:\z<ˊ܍q\c 'b9r(.Ooh#MuV2T0v03VZ@щK9ICkpww4}+z#ß#9gFsBV;rF!e>V #e„4 sW^CNF̫K4/)s/+>ܟ4Ʊ(_p@C 51ph[r8С &^09$X{.gkY;9~vKScx(o@' f‡Z&|$Be`§@[>3,9|qwD]Li#^ e=e/-C1*x`1}-{qNJ 2 x.c*~ϲ!k p'x``F9-wp 6p'q1ീ; Pi!s+gaQ7e/@8Ĵ_zpwk k2 'k2?<>I4؏&3]@Xr7VmSwOg ޺ih?yJ鍼/b{pi!2o/թ\.TD3SG W{.7rlQAviOq~wYɝڟ_7pe߼ܡap,(5VsBi3m4H =Tk+\Wz T{ cUʷ*|t3N [FLWm<;^d2s=.Wx6'[!euߧ0!_P.wAx]&uGv$86츩#4e' oiD#}gXPp~AT,4Q,&mD9o5QW EEDŊw^6,_G-{]|8(yNwد(RMgx׷(e](z/{? ӞI '3g MYBYo$sOG\?2?I*{Z[5柄ܰ1}I~IRt$3Ÿ1{-lj9U5軛8meP9w*I|nc4|x>[B'KvA)I⏽p0,c?Lqp\/vתT})i´MZ3||Y #'~^d3yn<\_8>8c2ҷ7vH1gp~x3&3}5W1R ˃֢ӖuဗCx,plKZ9{v[&KB?8dI7 ;2+E֌~?'N ӚA ӿ mwySs :Ow7Bn?d)3SXO`>! 'dS())tt[guj r}f۫4<6SVVǹȴ7a7#T;7]ʻѭ|it_IO[ٌxQ^YWc =@`vyyI?/Ӝf[<B?"?:"9S6M'z:r:KTS^L%zIJ2YAB3J6q߬ƶbg:' 1=2\+=+LϦ4j=Ө}?FӨ/Lu?h\m:SN^i4 gMu^7;S}6m12?ˤu=ˤupL&2i<5 3i\I,d<4Fz {3|4 ]3Ⱦd̤ٗfܤ=;.pvP>E{ZΔN3QC3K{UdiL1D&IpV\GiHH|-ܧg=&~rs~ g #7i&|I%*JNP po+†OZ*;Wv{!Ω2_f_ZefK:S*/A cԛ~{@ߙ,҉ԚEhCYl5ūxLm;(icG~Jߵ C^`i*Y"3-ǟ8KlL{}8T@I>妽ChgQ,mz~IJo gL ӧek%9 n6ﻝ{x./yE][˪TseË_竲)U櫝_eq= Qi۳ܞw;)\mclжTZTz(|:q|W9oid!U~wϯn=V5qMY[lFцyfdmz7rJQ:kl OfhJLLgWa"5tE9kpޓŵ`.߼o;?uD@_l+dI%.+z5E=nVz_Ux` {^/tޠKE3;*}odӌ u!v;##ITk\ pX_=9`>;88 eȳ; zx,+sGq%בoq}o%:0=4}PAy,S=elCIƏ&Hjb9Gy?q%"? 8'|"a!eB MC5氮o0d+=l mZ~!>t˦NBg?=~{JۢZMi[^ _STQyߗίP展-%Uލn <ܦA/8%:[J5uurD0G?N9S#hjD? YO֥W';Ρ6»3S޴ik;yEz2v_'lJs=xUS>֦{7 3/ :eޛ#初Ӑ'9&%yyˀzg@06ytIcQqzyG-oA4L߫i0o b{^cSd9w?>WdȘB<[PٳKt`<Mg< ֥V\N0͜=hdI:X[G=AϰӎPiG*YФ~X3F (<^Gwivxap.LKr\Z.%]+ښqTwro璬:y<~8ij#=ѝHluBهyg/^",po7P&SLKG14clGfn'=\|'=\\>oj>nv>ne>++'=|'=|]'=\|[99?ygGF2-jˌ}Pe8Ϣ{~g+^o?2r{ؚA'}mYs0 ܘX9(ww0.\Oҧ]t\:>X n5s , d#\FGv5mG-S,w\?^32vnl o!11&)t>ElS <.,8}!;~^Hc3x]f.8G3eS>g!?Z eg!&)w!żGӶM>Z|K0[GD !x /T҉ chm+bC.\\ߨ^VPīX@waī2 WZ]@U:Z@LUQ5\D*lEīRxU"Q]U/4E*r}piרkI;{8 6?J]_<"D2w7a| _%y c5t> c 8&1g/ ]>TJw^3j/ýA&SQߨ?Z\/~wLYtr-{r^4]*[ŤC}2ˑֲ>Hv˫X}ϫNwz y1z XuyZ^ pxŢlSv_WA(ݳ{[ߩƻ..sA4n)$Ҭ޾PLMхbԷP$Zt^mh,@@aAPAFd\ SAQQIBv $% PEqAUQ@6_՝99?u][unNVn)7nv6@a=m[b03eg> p7x?7Q䎏ޣ |Yu8DG_[:M˶F9P79sZa9{:=Y|p-5C ?üΞ%g3^.u)%gT̒Guv]) I 4DY~,D+vjR4.[_rN4v\7d[_HeZ :'>QL+yx$/ *kqM`̗e?4#y=sg,FwyO0*al{]nr|'Q>2ƴ¸p ~dw7@swlw>HſU>oM~;v)|DэffNiCJ {e77ru>#-;aaIdwCKe3,At/p4 m{|T?XW^MG/c+GPG}> vI;+Cg6?٠Gx=Em aR܏2f&''%ڐe;s?V#žVj5M.h`}Ρ8 rJ_h961_|Թٟ{\jVAN>ڠ mT]v Z# n_tOt|n`[|.9i:. %C($ҭ7񌒃LgCЋ3mv-tɢGI:Jd8VD3%{%mNC(uTRJ| ,>C`Dl_G =VE=QJ6 _-휉y ׬9dY>q/ޢvF>1hWɧo6kfb6Φu7_ hD{{N̦F>8)'Xw S?%*)RlͫW B<J!R#y25))ٛB>) K%LTĦOf`*dƦRLK%̻YJ>TlN%̑TE< Dr i^2(&ibg`y13z۷6>HSޝBgys#l%3Kpm' aθu4~t(`ܓaDk&FCM{^ 1i[ʋ<p_S4:+}pAi<6<V]wg5uO@?ܜ|+Ip}ҽZpp!"]ҽƔn+>Jflv~k"C?5]PuN26#cOt EI ӥ?.a֟ }|zˍRq!~7Џeb ҳ f}Y6\5=.y+]S{)Z%ת!kc:O~st9ч^6l] í0@a\6xϐ Z,}Rצ,asqĹE|ŷ-x:*] i3ZNg˽kM lwp} Y3Ӎa9ciy~5]]q"i6Yq3by貀r|M2*K|G\dpLT&~q6)Sʼ{ߋ>E-gxOϑngY;>K\8V̔{1}2XyʔsUǙ"uĤwy\:x#kv-n/bҵq 7 kr?Rk-({͆}#^vsAD9^M Fǔt1 |Y#bZ`iAeJiQ+Q)|#fl>_Rn 6 hE7i{%)9'O.tq8C [+{(S{3hd5XLs\|Cmo 6~s-Iߦÿͷo\leDh[x̕oÿW~e@|Q#fnleQ]IܛXw8ű([[wŝ8O9y~~pWk^R;ܽRRv"筌 .sGn'͉8koF$ޫvη̹5]$:󤯼a9{Z[x2W(//FG-F@}E|]/;~ ,糗 p~lv̦zM>4tc٤W_&Mz({K3u1mKhi[t4K磬8Z#Ν&a5|1?>f)oOy/~)笒~-=VS }7(Ӆ0"ƽ00_l.,v6o{3Q;t'*ʖm,+ X@cK_cڥ͘b')+b+~we\ {1<޴R}+ޢ}j>s*B7WX{o~|,uzczkuHQ9e2lX( ,xSܝs0 62/sh@Z'<]ЇQ}sa߫r9awY?r=Ca\Uf8#enkLpgF =ҤClwU?QGsr|r(̑#O칏1Uc̺ۚuߛZ琪VM)qng=MDy5;._sN~3$>SW9.˥:EZ>ƄQ< ϕyc4+5t{e0Мy{s2)W4O?7W`4rs0/ժ$w06m)SӉ~Eo|T }Y.~9WYTC["e+Ε8OYkN>P&K_YGvxrkoϥ mW|_a<ѯᰱyW_#[B{Y;^vb qyuy|]a2rֲqg}= n /F>qپlx]X ]r\'k^XmA͡º."|&G~G{ qkʮUbkreiT@\]@vHS^@sc {Tsaů@9w9Wplj]v7!6w+Ÿe@l<ܬ]0Z9`s%@S zlԋN'/c߇I_>_0h8vQ>(x7iXZ$kgЏPm Ϫe24<1_m/Eҏ_Xp_Tx5!³CG] A}={s܆K2}%ÂaۘװtBN%]+;[w=z<1{u7p۶SZ־%|?yuJe*\?J'Es wUpw)pQnUT,ٲgR*sKI6,gb_ :FR?3lᕎ/Q톧e<&BZ˄ǝy'hWhT[; I#v$B I.($}]VHz=U9 [u=ӇS$3!JwynrjW$vwL.E2YƋE252ر&#CtǙj=JZsh{_{l>;}wXփ߄?|)n-|P?7[NoJo}vםG+DEˤGy H/}PTB5 W+Zr4,6 g2>>Lycjߦ 3Kdmw}xjPiwݎ73~~ ~aZp@`( ~lr8KIN0+ױ췦QoF GecMFyGJeړƫG,tAyK~Ted/-~2L|#.l&iRf TlOf?xrIzSrO/yr9巨?}g9Q;_N{IUмuGWQRATQ{ĨȺY,scyaxxs8o5lx6 Y|BP_`+Rl` ɳZWeװCn[S,V7h_6չp[j]%{J^I=dYIh%Ԏ* bU 6Vݺ{2ߓE90݅ ߡ;S)}a2NhVu|KO1><eU~uZ 7:z;Û K툾w0o`z𿅐݃h }Y(>D?/-POR<x *TDw׶XkBC߷=Jyq$-2ys.k^$M{/|oP ݓhi+0^~n/^~ hP?z~d%"~fGd̿ɘÓ1$c=k/%d\{]su;{=K1/_뜁wn{@GT{ \HM̞&=Ps*0/>LGXܾus_E|O& O3(.&L鿧^=V_; ::%-@rMD+Ⱐn|%YO |L# <x-$ⶆW)5[ŻSx|lT؆{Mk=^i(- #Z)N5[M56צaLpZ`y g1p?-FFk(OM{J#|UܘJ)dF;﫼r5kT+pb;{4KvE+Y)r nUNi/ؽQFpoۋw\pGlp$}mN7%I}Z4|SX=|L݁kNiܗgIk9"l' w5ii;847UkR73s {U gs4먽.{?:Wzn4_ۊ4>3bէERRmcΥL񻬿B2J'brKa}$M6}.Eб}㴓(5b,O[V4i~ϊ5 ^m;|բ(qԣ#.J5[wsz\p׏[xFTNDkĊݯ*>_-4Vl^܇x+Mм Wk/nq?$Kyr쎋8/4XXڇu>%^c@LiI3xQ2~JJS"~% h Ʀm <" /˭,| =6* 8N+)! 2܃}9["ҷ:#b K1,q[{3x)acIq6~]O]L:y5D^3grYd9er^}4?Y)nmo,5۴ пd(X?<߃oyVG"=W_\*A錌q0WI,,/R[RT<ս:uYשׁz?duN)l[̲?d7C76Ȳ$CkHW ?sȸI{WH_9q*6j'fՃ pj6I_=XvNr\CBNCv$>ngIߘg.>'K|`[;pJ22MA +I<Vd{Dߡww)д~Af#VIXuiStid{>DX& 8Ul"K >'?+8%>G |\chY C\.#*O&bYüv#{8OwiŁ~|bڣ Ww;:͡쪞#E$%!evvvrqʟJ2~#ӅWYG )߉+: ?!YkAÁgpֱmeΤߛ̹" i='s]͑v],dNpM9-^pm!Ir!oE$I2=? 9 mY65Fozםu j7ZulP?$D Ls*??s8#`$4`nUxak<8&^QH;tJrCezPϹ?G%hƵ̀5R MqH=:|aQ9B|.u<:x U9#|d&4-0?"U|0f\hJUjF|?)yE//75$+8Ax  2S7cf>7q dU>|IH-|9V4Ꞧiϫx^u8:1k?~~yr8!]|e8_)Wh[`??Qm4e3ϥo|=- ESS~}Y߳hGĭ5sAMh}9`η~AVZ>+ BT s=Ā|px*d۩B>{0r\ᕝ_W<!p>&Qg+.L^*ꆿU¿nړo6Q&mS܃zw9Iݼ|V_XYeI7Eo&^9Tv,YG.~sPۼ}$-ѿm{ibMO&HW7L:?>4Hύ6zZƃ>1v}{ʮY* #ERBe6۔O?Q]=t'I6VZ>dE)my~ >9g#GS$B^8QCzm#YOh8gHu pE1 etҨ%K;D OEYg5o 73=n_`3\H=X>/`jeAbަe5Se7vx)[jWaYhY/lqnN, p7&~_2X6ڄ#Zɝ/XY}0ҡ'ý Sl[Sخ O*~/H[wmN06A5$JQfWoRQo X{ޑ)&٤Yg =,Tj'_g׸|mob) ,ӫ* "MjQnkZk;FONԇ66R[^7'hYZr*KTv1Xv *W_xqhm[ao_9t7Ummu|\m /Qle+t߇qKUqa sF9f:շ{5~.B 圮dۂb54!'S'+x. FANz lj>~r5MKN|fjwøTl39iwoȏr@㻅.N8Y="T4ۅy4~py4u}JqZw}~i#`'o6pw48n[p܋ળ* u<6Zk~Y/u~Icv6]*,]]5e2ɁK*Y?XU}OP}3y  `Pm4?+䟻!ݯk<ɹ \9usx;s"m+TJߍwfY~ٖ)_3N#Z*ua2!$$!pQ@W1AD@E`QX *h@("^"pUy1\X>D$@&[U}f2MO:(oR5NRH gΣTM~Հv  oUpO^D^]\5^ 뛫vPGn%%*.쒞<(Ht 韹=Eճkx$ =#ﭴ{V=wѻ Gwcy=ѩgՉ+*8t=0e]ECa՗a Xo~9λFscx't;wTYq~ e{;b?Jc9cc/`*;7]EhӲFm~9- דy hi)ti#il̟P;`ܛ ]/p+[ \Xa7::kMglx,㒳|^89:SǛRRm]bocC!35F #V^svoQ?))ip1|C%n6M?a֍?gxԕ9!amV ,"e]fJM܋{o XZ/.C*UTц‹ ~anwqY/cro{_7k>$#طZmfO %o"NiM{kݢ=|?Dz;v[?|TGaJxOL33%Y>Ykϰ3:?s3WYzIjio~>^Gyu~UO).u~؀mr8_׭궁m3;(uvտ:cN2մ:;6gj00f.?a[26qx(3%I:3oI; =yvшHVQ-pO(:Q]9[źGCcIoy`_n(x"WDpJm:oRXom8&NvpENIfK$okE~f!|i[%k&RY.3 P43EKSw> E~X6>Cz(B~SE ĠNSTu+wA侵ݖЕC^C`g=8 >OC$џ?B~tPhLŀ~R^H!O@C K!~:/!i5Tm 3)@76XRŽo3? e @Ql Yn߅4,_FF}l$35Oy]CLǛ&yډ@=m*Wo]3S/,E|;s$GNdMΉώ5X_>{vcJE>嘦ў[m9~zE QF{^m ewcE~Ck،{+0u,u\$X"xOE>m) KWƣ"q!#RH6h ̗51_>`Ӡ_b/cMz4Y)pST0 iz~bG]S>׳ǿ7Lkgxyl>( }$sE u1 ޺MUЖvч!r.m?C+4׻BQf-r\9@y@*,/О'T׸p(5k#.gj5_ѪHijk"M;+іv\O=i3`ΪtgmĞ{c*~CzUO:]g3οY˿S%ۺ`^^μ 𻑦tis:-A7.=  9~w"]]{-{>saO|(?f.&Ӄ*-=' }]nf>b>_FL-7.+ ?\x[H6=ڿ8N}]כߙ2u=hŇ[1cr35W*;PI9x6X_ x;Yc,S^cBk'4WL5`|V϶3fGAB7_+` rh`_hOtAЍQ;=LwAl\$(`m+[Bo ?/J'c|h`7' 5Y3mc;>+$Ya ,M@yl;$ii&HD HAjgP!j?8qtP)Us B+ mSu۩N BtEh0C;.n_&롎+nL}]rs&z472@%;;X4\/'sz9:oH#fqi])* ɟѦvz,KƇ57"*_-;iԞҨ:ӨOs~mKϳ4uinXtXc'|BmW6'^hzuHGۢΚ6/M6_y:mnxBs;~s~Dk̒c}E u5 |vT :xp\koX`ί5cͿ>gtu_ZQ+2Мd8;%dtmȝ@skmщq6#RtRI7{t|]zE~XzUפh=rn6^Fwa C5]cP^(%׏B}}Pϐ&Oyca ap-3\<9 33HoAzcM鍏wBYmGcNП$C||%IϯL]Vwqo$'uP?gY0y4xs)NO_{]B􃏋>n|ZKsm`eӜD"=Gd[l yT@}{a&sʦ!$u6K1#I Ooւm0c5<0>Bz au2=/?z@zS9Gl޶W>ҷe?Bf]īo7p<cg;cgw6Ց7 tQ:n\۴1iV W͍Rfxv3{`A3.<1lw1.` .G45icGa>1^qM%u*9C[z[K^XsB'f^i^?n}牼Z(ґȓ8=d+c.x_.ǘRM`)La_Bl/ ^A:;`k)':u 'U:! ]Ӡéq!rf㝣rf\߿88[!:2Fk?g cNX>:ṨՍ/DNx>J}uxvP'~׃Qz3~710ƨK1~wc&@?U޳Bz\^Jf2k>ܗXüAd3|W7^`O;rKͳ Z< Q/g/c4f?mxn>1ބok*W yRwd1vjMҿ"/~VyLjڎ#;>f-IGd$iI>ڋ!Dz=ȋbwcràzAaL=uX>ס9Na B_]}||̿Ԍ֡}~Kbx>SHQ+v;߾VpDitOO6.OM Cz8V66k;|jˎw׼9_9/|E;zz3߮Zɸ=]8ZO2Mctp(Ghg_OQU(Bh+ '8'd$ʄO@vvF ?A]mi- H. 9\@r\T@>~$ cu@NA{MR@}&8 } _>ӎg:12s5D~585rgܕb ~ s5=f:X`ʉq߅~yG!"gt@*R{Z{?kWU_t [&$AA=ݍqEP qF?("+ EPY\UWu_U7!3}N'_Wnݺ˫{޺p9ƨQO]7.ɧykgFKKI+#L` U+۩W7Zl_*ڦl,wZowlߎ3TOT !;>8`3v5כ"]?{(͛cZkc^nKQ<:b7a%nClXKKBKybTwX?tϮGnLnJex}T L|~LeZ6!; E{6ӄii³Yd !M CW۪N^MBOގe&}#i+|{eI\\*ãWRxu q2<|VM~TxGn;KΑ4e']ߤ;)N??Q{tOo#>? %nk&>-еg>gp^2[*/8;M|\{⤋:4Nuz}(OA]/נY|sr\Ƈc1/筱]?Ө<6lm_ [`Yǹ 4-3c{։ۇ>QE6]BZuҞ}h4Kw<[23S{L't%zL۬a2ʴ]2^Ϲ121` 0u_؆{hNqH7.MEAh}ncӂv=@clP{ٴpdžcZ5Jp15c_J{bƕ6ܶl3_̗o~s7?zҩ7c<[3lMZcMQGO^춄LjxF_dLfm2u{{(raGI1،)Β)dOGG_3}qb+c8J/_JU7bcdyIzV9 p'UcŜ[K7`3"w̌WQ/ݛGŬH*]`VkۮSVk>SZC?BWzk3eaO LmTs2`x>{2GFFcy<dʸЗ%FY0ʟ"ah,0[es1f̳6t0P /}̳ͳǛtO6~&K|/duaXkqXguoX/yϴo>75Zy }Me^GWi)=w pufewهxV6;fӻ2ލdӻz/[d́> oQ&a3Ԛ@;Z1]7NrΖw7bS},NNJ٦Hq\͏y;.9Gmlc'<#yrx"pe4SrȆx~ s{cG $y@9mCC*#KOmO|V #Mgsdnj^`tu ;q<ūQ U} ߢQ{+|7)vu}gY2޴tU%KieWNX>lV3쀴̻ 2rxIyyw+)~-{q՜ +Gsd,ix@ssDk'㯿<iX7Ø1k\nz 3Ehod= O81\{v< @?̴Iu;dm##y4{*q:xBw=s{5?3F3|cZ섀-`Q'g~){ eCu{b?=Θ{*GaB7bGI͝g?Yzm(~~|QG!o7d`Stu%sJ Ʊ'3̏^I4dy+yC޳qx|񳓘o3:݇JkIT:[QGߊ'} OT?4ߵ|_=wlj)@ <_QeRE_Py*9g"{ CdW ب kM|NjZxg3F/Pm|nW@b>=5*C1}7D.xֱl.R0\.yѾ?g*y<Nh+7CO/&^y {r8w do|18'>upGVO0F錑f>zL2j=Bb n1-7{&z_/7s}k<<θ23>WYk}/u+e/CoS\zչ=:@V,5,[ %J%[%YJe3-ЂeF% ^VQF9)Uo!>`)sįh+u+tDSΎ5:D: x ,L;S~?"ϴbmR,Rn[l[~b٧ZG2t?INlWhUM>, 3۳(},Q w510t;19 9daӯ'i540y2|xZka`U%5(G*<\OU[ 6}UY3ߗsֲwYۆhZ%0|(=.5Oe͓cJ~'*jG@??=3U]uǃ>;2>_<!fq!.YȾF |wa?];ıf>6-![ ]z P)xC+[DܤJHp>[זP!%=T"PJ()>4Db%l0ϼWf :gn '[?R gf~ }LܴFwJ]ױ8{ ج)؟H_*^KeGY)PXF~ e4 ߞO1.!3nan|RK彬2|0-^i|_ߎ15cvcCc6g&XеdyL{`hp9\A'8}ֻGTz,m5XC:BcYPHU1b|~%&֙ebcs]>O纸}S\3)Boށ|QF3ױGO0v+fOZRmuwP[ʹ/9?ڢYEVܼG۵]n@5Oúze=瓐#UQc:ea yOʬsC]*zqҐ7sH,^6imq_}\{#ϳl+`mWy٦&_l/xky|5Ez>{ *vZ z0ssy6K{~ \?W|Ö#]*o݇~~+ٯ+S!q&W8޹*(ngJ{<頻F_y/kRIʓ x!?Nojf7RP,g)]஑=ƻúY)?0]3׏.2osfddg߫ Ne=ZIvJ3EZ1=Ͼo {/+i.iV:޳2['̳?iP8moA#WQ>| Gh*yJO~',s?GUrئn>_ kaSu"1q6i<+s:<(~4uc#!àl#bvQGeςl /|Iںe%~ڇh<*C+P3$D! Qx_d[B4~HH; JqmQ.Fp~@ ΡF?Og]L"j %ND=/(Qz_~I!QϱLsM!2uzۂD66'&Qw2'2,dn ӇK-IԻ14=θ1 Yv; u惏3᲻:S49FA]W>DN3nT?ow3OԳ#;G%:p ۋnuGD FV*h~;GA'Qm2윤kZ:]oχŶʚ' OK{%;m}gu4b3]U¶~^Df?&kxMtN}~j=cdE;o؀ޕd>|7$Ìq-EOsFy6u׸ ;iE?ީw%EJ!je?A#CA B?vb|K)u\{J;olIiϷQ=i݉[y A՗h/s%ƕtӾO~o?kb0a/d h0 N58> `p?|`Ei[8|Nd9={F\_t^ \{C <{nG~| ~o#㻊螣8^F~Υ ^g\T4bݽ۠% 3mnPr_EI/HA7qt%YSl浈oYPx\+/yxʣ_7Wv~KL¤tE˙GG_UIC%ᕤϩ$|ӯƑ(Eg=.{01HqC[7y4]~r`fy'I&|9O0xyO5xO|IG/%鹚7{#;Q\Et>}F:Yuns#ߡXi#p/pՒ#jFE,e^-Hc Caꋪs}^ {TWfJG`Okw x}1䫌qa{G=kAGMw "ƨ#q|I;ib_8Nwv G[@f͍Բ 嬒Lb[;Xv|}~މ5_ ˗AmW%87j(to1U~czT#^h3sx~z$sGw'>lxofv=7TwDžlsc?buLtTT|TT|1W utMΑޕ=~/ovmZ+dlFs8>~Wjv7J8zZY-^? d[z+Y2=*]/̅tZgI-تZ'qßB< õtN1~Zq_~Q, }4ǥ60BM:qB< +KCy(]KdQ1j4|X@tws9x|q1 ǒ /H@l/1 EziechWw2xȀvȸPt:ά^n\Ye;(ҽ3?Ƨc0tĸxM: GY'1c+{21[%Y1tư+  ;zst'֡l?|W(kx vc2Ү!Fٚy)ukƘ!X;3283[,w)`\w`\F|#?I˘Fi>'s"Xrcs9 R}2 0?zoUH=n7L^,^eeior/ 6x3/.Or]ܼ;> \l_ >>N[+~܉p ]Z'rJ}4\${=12h87Oó:ӘO_F_UlO|`qx[ 3Luf΂δ>W@X!ס>et"g+#gQ؇>'8~lk_c7;:]6[pܙ9.z.\ E:zgOAxMᳱvK  Ps1^(5 м[ F9 |/\H UJo.]h/K3 _EGǏw}ۺe9[+۲KUuJJ;;_梮zVTwX:vճ/w7M61oֲ+0㮪&_| >ๆe32c}6zhe=ű,5t6wydxo=ջc; M{TGOZ9d*Oy'#1]rNbF}óY!dE{ ƾ0DEQMzTd-٥Ȇ2lFb#Ȇ9->l}OM/lbcvhtN>myͧ<պm[.~XqՇ˖wN|wIUWN'gG P'v:X:]r dpCԚr¯G,'rdΉ>$YgQ%1 2hFΦ5ʉ-,CJ'=+Gߞ7$}~$}2?`.F]WJwfSGߝa^Fݩ#+sg'|K5U`W3/];8~& %cw}eF/< 4eV.O.2+/% njp4 a0|(v-]fzˡ9-7prsO5@Ro/<9|$!)n/t̻9Y,s <7^."[fV+w|/݃Y/;C/ף>,SnMM xzOὀ Gwq>I_gP?όe:'<F!8eҳ[ k1MsߨR/-D;X￙exn#mgA(_Ut >wD3 e9{ЯS{pVίGa8dwv i=~_x9ύ N@ΠXdK_<|p"{H@Nc;UWE4qyoh;`; a ܛ ƖEM@7l-?q~Mm55ɦge|sk]G[U欍)8\zvRZ=CzӼiN>vJ50C{zOSOֱn}h(~_-cYh yNzbW>FO ^6V{5 e}x6B]+y`Ͽ;KS"ݤu?ݣsY:xGBdEFgo@'7"-7L݋Y#UNjk>@_T=.UGd2c#ޕ|Tݻ|o?sϧҼʼФd9(c>cc e b2/ar|{x?5=˰1 ˨Pld@I{,?~o7ioII[2.v[[ʖ [Oς4U45ql4Yx>nW,{(Oa|d8>д%qnIJsi:?1M Kӻ^J15r\` iz/6]欗S|t99/uGulWVg|陙Ngtk]Au$_|ϭQo/%]Ɵ|}?&=gI}u }/eQXZ36]" /8]¼tttt7c̨akc^O|(TH7%}z.O,]jS, Ҳ#\IYX"U}/s Xߊm69ї[~/?_:F%4>m?:{Z7TM3$I?VlY[H?m=ɻ8wgY ?Z_nq3,z =~eM7fh1Cۦ=2 =n AzړA7~ #u6MW5] _ӕ_Ӆ>\3lƙ24vbw i5PVb9@3GџOˮc}T8wϿ$иo`[=I2g)ctOŌgzaK?w& =*'UAa8A;ݫ:UAEBN|ZŽ< |=7 Bo}j_<@*R97P#rT톣MgԻsRtsj{2{~ʣgwTeOz@햁W n?p+NVt$ZœOEjc~_<Tg߸ mLWZӛ=ĖW)tcMMv|apUCc`Yr& vb/0gl\ҭ?IxP=~8:4"Iqlt y;ٛFL骊f)%R3}60ܣy)"աw\a'9^@\] awk͈y\ݻ:|z@;f i ݏa]ww6Sx<ἻWsyF >Scs7B\uSۆvl{r&||d\A.v_@ ֹM˿`ҍɳ֚?mBeƸn\O&Qޏ p#U0 湮x3?;Wlo|Jh([cPZ'ܚ1tz d4m(qHx1YscvƸvz*-_ϭ>{ r(:t-'ÌIV<h ߚEcuҵY[ȽcPΌ,-7:NcK pެ1QO3,z9Y.͢,R~/>XƛbS7e߹;<7Ԑ.xw=Vwbȃ?}oq&Y?Pϯ d9&~Nivӈ>6gkCHGv(dz%4]Pél1é27l8{pfz}FgDeGaK[ 36ŀ=1Cpjsqo Qb~#Da(. </?v  xAgFh?JγwS=fQ&o5<6F-uFx@/IwC`~_IȀ۫H]sOkGؑdȀW߭ kc>u̇rg#h7Js5~i$;:TᨤG<-Q/rڭO,3 N{p\UXՕD`{$]>}GkzIoh'ȓ.|(=ۃy~~(7j:6imQ&w3ِ^Nzeá!Ϗ.ƏE6r\VIs4W-3xi!DO{v*yQdFqt%CԷkنduV]j냐+?nSq/M堸ٔk_qjS8#3ʏ}q.}ǚ8p? |c\j3;'~@^ 8q |^ z23AsGf;xh*kƱ-yh1L^Up--|qp4GS[hj k1C]p}8]/u&%cܾn 8&0k|{޿c<*=O3c6sRԮ}P_4y.|lyb'FxqLdoM=x{:cqwdyl Yc}LVdoqxڽlFUy|">EGθҿ{1X}TA:\r>Ubg#snX zO w.sƒvs,cݲyLslfi,T4Ϧz %TܕM ?XmϦ:/ev/3?[LBCm׆@=vA)og֩طH=w@RU#p>M#|/i2i|%܈=_ha&ڳ{_ D sx@in_x%}jt?7O?:@dzO:)oIwoޓ?xƼ.a 9īWC-9{rks.Hk&},'q"̘Ho&^UiD Dc:>ԘDND'=:&^z$/$w=6'9ziޯd* 1}izmqd͗c\uˎ˜\\}gs]]vc̫u8x BfM!^P|4ʗS)_Ouf**5}*uT륩׷S)<+>%Jˣ&Q\ydO #"l<:uej6dJF2N#O#M#6L#6O#vO#N#M'N'BI$$ӝIgLL_O' l3. ?d |u3. 7Lf]Iv9{&写d/$L3."l8?{M)1OYur B[ Q>SO +xe}|ZjeGHpEb̹¼z@,x1D։_!d/Lf]'V9%"K+>l\Nf-x2<$>ȼJqeя7YxNerw^nrGV%d+FFĂrw N,[WT_M:{<{ a _ ^fsa96x O_sY5rx?rrKD>*rxLiB'|3FR~6s-NY-͙ߍ&pX1aoS,b[JK_?g_/8:B|bbɳd^M8v&jدw,%1/|]+1^;3YX3g;X{}*OaWX|*!@C8!جUʚ y2 ,J|_Jy c^P <͗-Ys{ ;8>@w]6}'J$~1^po Ս 3>o9e}3rr^#% 7}n}2OO>yk7+h XcrB//t𧉤ke1!urV$,Z4=7x/|,gH3$Vк w"c9q&Y7o`\C=0 qoſc} s֊5Q'~q>Ɖ`kϸ=&#GPxGU'ɸ w| .3 >wܢ]ˋ ә12zgp{3X]zXw?&E?o?о|ܧr@0OdXK[{e?]s&g.bwRW I!=-<;gͥ^yj$i9*w /Wy7=^NQ+ut͕QeKh1M+՟h[Βa%qܳ^ɀl ^A@%o$ 'ޓ؏[?h柱WA}gG9G)Vj7psΒ%+뚕uJgѕz++տuwͻAnM^1_ \9UP?J;$G~h*sWi_*j%^{zbU&6|%kW)-gZWN09}'&Wc_OK;\|ac3.w ƽz?ءu%8Fz%F?L+Ws⃻O%X>x2?bd~ o$ϯV*R-'^EqRfoޫ϶fNȟtJ"*sFb_M6XM j,lGﯯ{:l5m߰ZuΙGS~sc:|1(f"/װ\l'/\WBcj!A&&Q^lg̋dc9+u:*ᣉ%) ~EՁ{V.o0Ũ/V~oWl/1ӌqWPpA^NU]GN^\O/У'X_-Ntj n`;k?gY_} 6 sa~^0 ^K7xua^C s0 s0 . ms`A緵a|5q9npo s,G 8e*cAۧq,h4 Y1}]CǧQibsnhzFs h>/=ո[U~;;*ޞ5ߴC/T)Лs5 %/1wZiQ_%8~kl_M}qЌ߇i/Ho*Yt)}(〵-:>_4/0_-##߬(yÜ:^ؒ=oD܋ 8tk/xCwI{ǚtf>/%4ݎg#+!tbʡ/afm3?ҽ>2,?M3,?3K}3:}HgGe0a N85CuY.&Ls7Wp2W ^ 5 ~:p.>ݣ L(;kW)"|4b|LpVKC8;['_1~U[2݂y#9`c{/Yak0F~W0o#֓خA:i,oL,o\M=WdY,S'O%(PfO+ T:!}^?{cu˿&԰|6@j) ԩLMMxku!ζcg#=E ,p lא l,V)kŽw`/XsL\Mr*Vv hư#yj\O?>&&zs\ TCd2n-rrrFCgPnaH'r ?+->ö`?AV ?9lǿ7rΨi3rWv/HC<=__{} O'<<;e_4ý};Mľ0B]kEAFW0nK! 3.b32A3y}𘈕duw7 5㝍5-'yDP:?)4n ݧo >oY'?AF/oUM4&~$7W i/uS7^"-O ᥂~F{r;M.+!T0u1zA]0a'`fٟ)k=c"~El@Bh)x/ rq:-]e gkƞF2~^Cϟ\4sߟ8Opcή%|=ٻs?aG%պ9מ3j)c*6[0OxMX2COxm7c^;93ser6.lS~uǔrY$ RU .QӠ .ߋ8"gtw\޼`Yd^[Jɵ{,vg8Ʃ|QTq<tJpzV}OU[-4w|ݦ!͋wU*"#áWchxkgg9b,52B4b`O>7uG4~sf|Xt=9>K)?,|b 4Uu^˳fS,w-#m<'}'1s7,u7C^aZ|Dd??f^{v7款ą7eliK4elA@a÷Ӏګ:lTRúaGL/b5lg8+te~Jةh^1Il3R^}7d˦6?-cEk3e6td5{o lV}o|{>|k뢭v5_lzF׾~=!<+hb9WJ'Pڼ L(RQNtz]S/`Y/.sz dQPzQ:Cw z@ÿigU]ƫ@{ npE]UrI>F>`b6sFmuxgF3#];Uz$u_:w+{1kVu!XLk\F(̞|Um>U&,]_aY0eƎ4|#2B[?N騉hMD[_T'}OY|&+|.Bk_ "_k-XL[c/5^Mw 2^- zELJ_t;)MaJPzAg.)E9C(nJ)J)DC#)+ : B?Zy'T}'ۯgE}-v(_F0w*{3pM[ʘW XgJ>jKtsho$|Ft DnlJwo݊"{l͒{\'&튼xĦ S*qE6mS">??[^#|. `l.$.ǎ;ܗaC>ĎAFb?k <[_-MoC8{>\(>S{={vb1WDh_ڭ\}1n -JnTmoGpٷp3KJ'J.{R=/m)#)K)p=/&2٥i)&6)v.PFOmЮڊRnĤ01)!ML tlbR`Kw1mNprdq=O/nI W wP8Q. we_R$vK._亿>=>=Vfc6Nx'u=_y4ű:d v.pէt: gmr}ômzNl|%)]pWXD}o_ѵnxO7 Oײ^[[]l?ߵ؞a׭'g*=og۵Ga;> k_1)ӽ<蟅sں)?Y9kEw,IBT46w~[]ӊyC&ΈD{סODŽgwɽ?7>^PJy;jGQn@sK ^̛݃jum.yW{Ju9F&(gM\kEΰꬽJ۫D*c6~jݯ{oC^p#;@WVȜEx0a)ͬ͊ GԷ9τTp]one'K~:9#S+lz[wq 81vuCN0MIN ˯Up_~B#`e*ضjUɶUJzVvq%mWUr|JS9RV}JS17Mq=gD0\mƌJ zJ}1*:όJie_Jk~Vɲ?P)=XͪhgUYq*NРJ:1B9ph鲫]V[|fu^]ϭϷ{&ɼ_-[e@kc(1,[Wc9U]H{Wv\VL]]eSi5jjy?*gRe>:_|nSx“ dPͲj^Ͳ:eաRjCjCjàjOc(=FvT"%ǐ3AטL)9I/r No4~}4Kuފi㥍^R׹tWƀu_h>>A߼ۣߏ!4|d/sL4^t7'vZ'߯Xm>y|BxC!x99gԁ箓I}ugAa=|i*ewXe,ȧ_>gOib lM0sxE\a ] |gd2a /2zrjp#[O݋Ùyδ?6f.5v;_0driB5vZc}vPc箩KiT:7&|f%[kj㤑0 eQV5v ]ZP#q{m׈)3Qkdwj7=8g8~^mx[@x{C;1 qAǏBl9@? }wO]_\#;6W`'iÝr/+Y z`s;9׸*v32~3i;(kwf1'@k2]t5ٙ8O:tRr 1Wt)3gi=dP7W}3A1nP*I1g/Ŧ(06!On^kPPs<_sbKfwf=ղ}x-\%e:'Zjy.z9;kyy-s:X-Ǧ/ԏbNZgU}i^E=MBS@(iҕAAE Q QAPPQ$PQA:J i3,|1Ώ33gzٳО.6-߆/-\G˚Z3 IYۇ2g\I@t]u+-Fr ّeZϙƉ~}f~ ~-g[3̋UŮ /m |DrEYGo{ 29սrF=r:1hEufu}?6#+a6kþ^]U,[89im;0ò]W0)+JC+=B- ZsWY+~#~N#nlK{Piv5/tJ؅W^+e/c%u_WOU2F 7WF בNKQN 0ޑ8JVkUh'iy}$>u^uFF}2<Ґ94_bZDW}'A99穨;r;z5Ӵ5P0 CE]uLyU^u5Բwg6kWQ3sh|cUzq x4/"=tmyOFl7xi9_9nẓgrsɽyOx_IS0?iRF_~PUYFiw:3(;[O qߓ%"ܽ:q0UXϕ1t3{-c{#Gz4x|Bt(M_ǞY8ȿ]-gƵЦ ? aW:|XH#]+ sX}_ש,a' agC{ MAF}N /SY4S2mVsj<:fz:S R3u3S2|'<> ^dNA} r=] } 17alyhu2S>/-_͔wYpCiXʯ&wF 0֓p2?goc PUBMN_j)qŴyyiŝKyՖ %}%5\S%qzQftgqeQWeq%3K+t5K'R*|ܻun0ҫYٿF+NWfqeBW؀|W6?@0.5_UWq/[Rܳu}_.l=EpމFP+V9\¬nՄQ9f>9>,fl=7QD}H3$Os9܎N6ޛm얣m읣m|DK?fK=J'׶tE9\\꯺wmwN/r7Q9zO'(3mu߲:Gub2UrdQWC9zgk^F[RLPZE[]8Yt]2Q2d~ ^>Ųt8}(Lc_[Hzc/bSց煋m۾j !0 c?Pw-êR]FjR@mzH}K#Pk;jX}͈M#ީFxD}Q<'Ҹ^rG\*W4ބߡuL/ecF ^/'oqƱ<:'o 5@X#\9W}=r=y=?'zt=?^Z/ϊׇMN[#R ~F: l/) |NnI<xm]ϣ#q.g>뽆\[?\G{xm\OP*&;1muGnSħHz7豿T0`M.`m/HCU\Չ*-Y{l4> =eDDϋv8^pwIn%cyvQm $\F |F#}d F/uUab9(y~X-t}zd7HB%S}Pmmʹ߃jM2ѯZ";r5>'Wg0zQs\C%*Yh:r߃srCy>h.?C\Y]po_ Xy텆\}E'%1eso @=>hf?,{crYgYM`>7;Tg`?m}ܿ 20Ír:ן'-m9bn3fY7~<um?Ѻ;&OДM55O~k|‘ei}lL+LH?7~.H8gaN{{ey{m'ds5YnpO  qs# 3[uU2rpE !2? !iXsm_/%Ժ.I߉퀿|~C0Ӂ_M5x6BYViH:^"| xe l2%W^% 8S7ex~N[Y"x y/K+2۴۴LerV&t$oW ];x3;Df"O_yXgxsxWo{p%Z'T 'vSCxv<#r})1\O K&U>'p**o/+=ԍ ~ ȼo0/&WPin#cV 5Cw:GcX^{n&"p8" \\x>?뇁K _.+3Hq'W )wo6Eh\U \]; ;k2dn\'͑uDpp#8?3pJ pCg |G̈́n!'!/]Vp]:t8Mwi)wn#2} ~G<~J_,2[SW]|K?ú]"p:݄=_ |R› _>neOŁpSmxȴ5~"3~OEl" I>X\,׃w<_ p ]_߫O~ESZRFvcb\~U t^tO5F'Xz{L2= ?ֲu#jm륀gT9s.X3o^wW˛X/a7ګ8p|p}PT/\Ze{&w;zrGZzLWGҖ35v`~6W,Euo_ߢ.#^9 G]73ߝP}h3#f L>[#n-K}!eru=3# ROM1g Q(o3xG|_yڲUg oQwңooC?fȫ*hf;ة}T}ilƳ˨//֫|xםE}KWS(@3:/xG{o*\տh_xvʟ(%g{#iS˔ r#^tA9uK geW49!WIf;/ִ4?_ )G9}7/1q3 w&x77E.HSNeG'y/^7Í~rGq?~L _6_03#Z|WC} ?7ӣg?<#mF}-I9|Q Ys=7yKh OKgf zV 9wZW)l;:E޹ߧm+z=d~F[k\y7f{E]lb{/P".^lE\q)".^ދ2{GqU"^ĵދ.{DZq"n^Mދ{b{/6lE^ĩlE|{^ĝދ{?^ĽދAB^ďq"^O3lE,{c{/lE2{^ģދx,{c{/lE<'T"^GlE1{^sދx>{/`{/ދ["^^ދx{f{/,"^^,"^[ދG"a{/lE+{d{/ClE|)">^ދ"{_f{/lE|ocqM^l".dq콈ދM^el".oq%콈#m"bqu콈cl"cq=콈m"nd6{7ދM^ĭm"ngqM^m"&{/N6{wދ~콈{dE&{/m"~&{/m"` 콈dE<&{/m"nK6{ދ8&{/l"~&{/ 6{e;6{k4콈?ދxM^ijm"&{/96{ϳދK콈ދxM^ċm"^jr콈WdE&{/콈dEk콈dE&{/m"c~콈dEga콈dE kދM^l"d콈dE|&{/b!{/`콈}{9dE\!{/b{pދC^"落qC^{WsދC^ĵ"uދC^ "n␽q3콈"NtދC^{9dE!{/{wqދC^="퐽C{sދ1콈pދI콈vދxC^9dEC^/:dEC^ģ"㐽{wދM콈vދxC^9dEC^3"吽qC^İs콈pދxC^8dEC^;dEC^+"tދ8!{/u{otދxC^"ᐽ.콈w;dE!{/.מ9x@&_g9wjdG ԙid?\b{5RgIU篁u߉`{Q~E}n{K `|:! '.CaWgd!νFn[tIq(S*1k*1;C偧J>fAy/Fyh=Y?O=!^_*>(|1 p<0 %/>&7\A8 0VQ= ? B*W_Byq3_AyoG< x4o{@yr{3P[(;jeJB~ 㷼OCy$Qeu塬?Gy,,m|s;,&_xӁ~@yJ¸x!7(T>+c021f$  :8+X6;eP-ڱ.:#-.Iۖ}Hx4[@ƫuQ(/cia| LSai3; yDy|^:@6|#gkˆA)' W @bki[KO$Iy1 O#牭!K;O=O{xK1Ky ?M;fO0}Qap% rᇼ1igI pu~kESΓ1s,zi !};T;L8zM8 c.@5 WO gq,{S߿DǵX'PR>,7Ʊ/>[̟^u oތ7鈎#:x>BbѼ#9Olٓ,$˾qY~R$~t?~9~~eɲ-燙w~'z'ì|/7/}oː{ky/Ȕ?2 Y{9͟pm 7w^7wpfpVn.{Q%|O48lpY"|c͌y>s֔XmLgۭoXoHy?6ԭI|.Ƿrǫ:,^?ݼም#6Ÿg\omM):|\H9Z6o9ږ~/]8̼9o:;sgǫFp ߫}+}zi[~ :cJyyjy՜.6^;߽]^չpͲ>θgd_೗ijro\E߀[=.zߡV%p; Es>  nKVp[\o>n5p:5=.\lQlolr2ZPڋi.?<.ռ npg0\|?|vQ+VpucW NυYzmWC-<)-}\|ͫU(m, sv_.n->-d{ {npg{/[܋vP+ځܧE;Fp{k>. =ɢ7C-7)܇}܇o[#X/}> >nQ/p-~Ϸ i&pv.ڽ}j.{}o< >.}[T~ /lXMhd,ãrȘ:MaLsX+cG]z:dx. M!p]%3ži!7MZ4 al̘7>d1}ՄQ¨W;,'c o2v ]-<:$Aa0愹cu+S`ͭ`ʓ' aeGye^MyB_+qv!]ǖul:g&j|ǟnA>zWs s$|\;GIeNeyu8ϊٯ 3([3~ opsG'S~0` sy>LfZuS]:r=Olz4~JHpo (K5 _ #᧥{9^~[`Ir/YI{0 &{ ^зŐo9@~Ƚ{Enj8W7|{n>q0@uQgwшq@!^㦑5T0{9@{{yeI !w{Lwp=ߊ(ńwo )BDp1 =m󭐢^oo.ͻ[[NJr~8||r1ΫpVp1^,[;|+.=ߊo[.kG~tf}6nw^YmqΏYQb7┿OŹ0o0KಫUn]\vO{9݂>׾duC׮J{ Kr|%9~%9%9Jr|+Jr|;Kr|Jr|v)+Kq]/ug)+cJq]V7,K`KOb=WϵQKަݿW)s}~&` d Pzmd d d t,hA"f;%b֢\Yo"LLg1ӍLa0Tc&1-+=Xr rTN$+ϲU˳l,py}<9^xy >J] /ik5ϵoz;9ǹ"N_"_"Vd;TdUID惊\""ݓ%+\JFJ+q>ugS%7'*]zUuSH}2kR羏qseݽ?O0~_5 ~M彨_*+HpKs~Fq|> 2?gjU0oG b<'>s(>O.*:7W I! r]}s@\o|T }<6ߩ$f3|߅my^ l>8{~ǶGqkz3s:rB1_> >mT0ʢh3lw/t1ʔrDcz>_-L뗍zu;0Q '[!su滈->/ݡ]D}ע>XE|~E~S~vk4`'u\:m^iUy*y«P,p\AjB?T*+>ի\oŁT߆iUw4;AG(J}E~}^6e0?ZG&JZ@- aXӸp™% eCY!)/~b W(-K' IU)|]pj>Ga|?/ZϫsP|?E5 ( Nc<*Apj O{Z/ *.}ɶ\dcu|oaȷ, j<x 8G8xp,yEJV4.Nn(p:p0~Su- د׭GGzXG䳀*~xp$$-#N_*})}7*}~$3p=. <-t7Tzy L {\֐~eN.~ < 8I5LӁg,ELc,3U 5|c^cˮ{C8|Fr5OߒcjM,p$p*pGMN{zu?`Qu N<ONBUHlmY7{uͩc{[])6u/^%-Yw&Nm7JU2囓ܗ"(箟b|;]eT֭p4m\mmĺlkk1zP{}Bhc>ses:&53e{pm<ӎ$L$ĵ /bxIs^Xj|Tgt5F<~c?{;QbxM s[Β2^kߨ~GMV0kqD*po(aq&3bߍq%lF:4;_p[޼u\-5܆ np#nݺ ܻ #p|\|o{p1?6{ :ࢷR٭Z>IYYYkf:f~:gڬC:Cd:C:7V^yX^'VG^Fe}Muxk_:^P/֑ӺV7UemXQ.#=Φ3 >7:fXԧe]^}n3hR:kuhs7OѾRKg~aW{ cDmvd}І^JrWWo.F2^Zׅ% \_oo4p g%m\Ƕ_c=hkKuQ߈_\Ys|z͍vjϸa!Abee,f^U"ӋXYW:eN,9ksfeЉ*y,m kO0]2~ a#"x`,瀟GƸ`N82fA_$<=Ӂ .V+׉ny[%p5Ӷ1-ջX-gm cpq~ODò[ˣmz:W;tX m'x_ 4'|L+wLcB]ՓNn8ޥ_?,~/ 9CÀ6Ru.<`=E]Egwpn#sEOl#G/ }UC Nx1$<兟ܴCp\x`+nG{cqo F|n2O>&ytƼ6|ĕ&vW{*LUf8g~&r)-?0ʟ5Ưqzz.mcq.)ǗE 7OGY^dnωc{0 a{6% Su{0SgM!?? xv]Pfuö]>s_ 2>I{QPYݪε6R>ݘYX\cq6رnKki]ߏ~1Osv{sźz}pVf\O~ooopIzw0Kv>L˃oA}l|"im+d P1GMǻ Z/>?}@{bz ]O?Yf#]xSsIWtvedjދ`ǺaqGVɡms6|oVDOl i6xIbK:{:YVI`LZ c }˖_02u6MAe\~N޶ߥoOzJw\>dA[~ME=O|>Kq28S ڄ_;{hOnvCyctގ}"^uɍćx^Gӆko> }/FL85Q^zNkP4{JO ^x#)F3pmew:pjԐ#؆:9o(4rP!'O^QCno.܆;-j֭Lw Ovu,NVCZP=ԐF4hCôBGFlPlҿ k}FEx݈m~kkSxXO 5YX2h)miw,7jbU~|1BR:G4^]t!!_ AߘeER?^ h.$O9t!х z]`!}NGtAM8tAO4]ȠЅ ʺ : Mu}T޿Oi v>9@]_Næ; MS=Yw$t5ïrƹ"Li Ko5 (l9T-S&$9ͼeނ4sKg&54֡m3C;uWAAw וΌ;|UZ~0dFl65.tY_o˺@sahq vsOkA]s62ͽ:ߛ,׆+\5k}o>: Wg>a~֜^}?9 ' 3Rsy>f?g:[!8 ⍖4=d5a}(VB-pov6V>L܂ǞG[*ijcϲ\4܌,uηZۨ1z5fcpqEqF+3'߮<)bZnY돿92_)a$8>{"q_?n㙭݆(o>SmJ'9B\ 6JyTj}NYs D5>*0pӧfxk췪eog\o%v!TD~g{N=_ಉR q9w n#z>p.hMoo"# ޮ_mp>4I>QmJ*Or#'!Kg67QzS+1H[IKd?+2Yk^Lz2Bu F;&֓Yi\O^+{RqrN|/u?6H|'lqZ~9S:6K og#ySnjVڐC[[}ؚb7Bglo-{n7ՎsZJ:r^]enmėmuo?A`_ n3=Yrjs (6ׇE5َckj+l+ia;-1s\  #:pm'<g'؀Øޛmv mJZ miƆab .ۀm~bo0xP*Ӟzv";qO([=}T.ۅK&\pچl~l6uWڑ+8I;Ó~b$=o5N{Xj/zݘw6eOH $k)DFb6e d^~0/?m_œ <\驟SoB?m,Ah{^ǜX>x\ύ2sjnDt> __ֹ/Hg2 )7 .̝@ ';$@8$LKuQzPE ߮b(\*LJ^]?FZ{c 8~xωa}Y-ol88*֛IoyX-e;?r[In_-{+_-;b8>mbs[F x(0ŀƤCB? uH$@Ew1FuFH ɦ/zq8Ac_jom@㰸FN<>a8w.tԬV!{!-(%Ϛ2wwYqXSu|q'\wiQd]_dͺ#,'^ ˩M(ր@Yx; ,2xP:[-p?FHaֶauトFj_B#7H6 =< wJ-RkMK{ L{G[io]S*ӮC?Dcc nQ0Fgor  UwK<#mI*4h[~:RmׇYoS}رߴʛ ;*}qO)+|"@ڇyol]Srj ]-JCLg|uggsoCg7Wga؏(h _r弨;"7Xc3\uv3.?3v`Fw}7 ;=uT[jf=\. @גwU/F'ӌU uYm#hevɾn:pPGcpq'AGJԑnoYD^Qs՞o2qC~Wn ㍀2~5xk1pw?כDU~+^p«P5 ߡjVx(qa2t;15,-MXčc 7s93m֨9J;1v^8J.'مYsOq1AUhfܓ9W*gI~3?FȺXlߘu"߉)<~|:DfRH9 ?"E#"lTJǚI~2w}ÉyH/kiKZZӗE:T~~,p- ֭mdeF9wtpe#Ɠx2k19Zt29@{6js %ӬYCl ]+1r?J]#gc䬚Gccd[x&yE9gm,Z$S[r7Zs)*D X+! w w!y}VOqR~yx)w,!S9!x{ő$x^O-'.v> W^dl[hfvMGe|ïLTPg*gyA\O`ζߕ~oYZ}ޯmqJO I ,I3@kJ:Zx|9fJ} &Z8&F(G9$Gљ}G8Qt&q(3G>OJ!FIF?|1Ju=^hFQxuImDh̾"%gal!U{VjQhν2cYܨtU^lF⧍zh͓*Sg[~e;ʶh*۰R2TcG}+S6QlS=8f4񳎟瘯FM:o4\hҹs H__㋉tT H:'R1*λHs&%9׳륉t] {[߀'ox⽭_khSj̑1n֖cyBj'Ou,<x=3%c6s(<_';DW)m~֤ixqe'X#趧9+y' cHqHmg/Mp?;fFm a&˾ žg\0 <ǧ|i/yȺ4j{|$˹xƁU~,sܤ!yDz\z!z9cڪ1!^oH=k L:?&>q{mS\u~v~6>l?KV,T[Kc@Jq{(㲞*)b3F&y)u.ߨt*ÿUm?5*qy"1/Se*86{\ƧNg8amUJ]== ֥="6{q{zs KF/JW8Qc'z=="LG}4eߌuV w[S^QگLiAsN."sR3s*(OPqiϋw;m]`<2lEY :1fDsW?,˿_7>Je[;mՅ⣬>ż=4r:hQDNsʸSMܣsSbEΕZɧ|W9{.q1lSy4M=oV 4QN=[=qsUvZ?~?{"?-ӥ}^W(ke_s}-m=_w: ĽUly(c5&]Aٙ)2ƹ4=n4iBd[A\k")oaǫ4%1;{IB/o4]ٙH c+cs?5#Vt kEIg8?[Ԉg2iK?ݣ,=jӪa ݕ.{_ۛ)y^@\;O˽*؎q_Q4?p/P؟El)^o mqn2gy(MxqN ? xK߂0= ڦmw"oMI>jhKl6y'O5_fY_Tn?ŀkoha9*V\w.ӓ\ᘟ_F巯Pp7+|p_{;+nPgUg>CzG_p3/mH]<#o?mHuH>qu UإCx~~QJc+ #$CD>?).EQiz k3۔!^La4S '3L73޶;C3NL!m oqg<d =d/UI:xjqC q<]^mlg&?0&L~یu{pNWp9zѷ@?pz/gy#9SXPx¿(Ux[p‰HE웆{l =t?{Cix%mb8>۾$GL)ֽ%٬;n/JmlNzSsP\=h¯\i&/1iޮ"j^~kf?d[vҾBzcz}_ ]l]m/Ijlt n@;P}LOΔ9y4rG&UIs,/gJ:f,>ԝpkg%=r>]}β~a!Ȟg?s~`Z9s 7D6˷ ]g띳XsϿm@v]ߌr@ЉnaQ72<1oH}+{X:vtcy=q|N_a qy3CM]Do|BxiJ#1_f>;Jw櫱W p ӟMBP>(?䟅 =~|lS"ȃ9w^9rxa^Nj "bF4>g啛zPz~]cs)6٫R_vuDR&]rj&R?ٝrB=@yǻ@vN9 Mދ21vjN߿de.M25x4{{&pުMl]ٲ$]f L:e=d\Y|/ g-֝Yrž )Q$-X0}8>t]0x-AxCԏa'Y x)s'oB4gCʆ~~0Թq86n\ͿҬ͢C2*Qϐ^˻͂v| !t*%O6;_cu0闓ƌǀkPao: ;VGmI²IflO?ov6]Ѯ&]p<_c̟2rc3~8Ơ̻!:G܆:e<繎skdTca^l~HW~fSxH&j|P{24UACwV Y? O.l{.~y'&iú'ϐnp-%м8yQeAk2Ww胀l1s;tECU6թw0/'7&SEoF,?`%Ɯl||{֕pՈ @K'/Uepjϣ)_aededhH|hjlƲ|mg=2=~2sd?cl~V+,)j%U[np1}U+cBy? N$rHU]˴}eRvn]qWV9~ kw(ڜB+UvǕVNa&g'~J_Ot庁>͡kX'oR:Od?K^rYY'A[X'QWN/wcl݀ cmyonN5{@[;Zshp8lyײ=s\ތe{+={xP!Cx\_;3pOJ#pOG|g N?^'(=sbKNB-pQDδOu͝qFS4p! ⩹vIU-L浑\:km)t09 W^ l0 s!x@Fbq*'׍@MzpA4qpb rp`ɗ3@d%J O g% k+xH_ќ_Q@~a{M\Gk5>ܩ:.p{+yJ%GjۅʰX~uǝTW/Suuvyꕪ^d\_`էN(*h_z8veJ4t:p:)j~i\kN;EpMJ$-[X^)ٖ7np[.TyoQC# ? 8rn,@qynzs6 Gy2{y>ٍ ľ4cY4ϲ_qEg=~_uo?!4f ࣞeG=>yF.?_(mI"W[/VO0S}mJ^G^U?oGϣ+L~PP- ﲮTc\5 *3PbgR|u x^<//|8zW忔^5ztm|zono~ oQSzK?V ~'=wl@? (86:Œ  a{أl`;d'>V6اx2aGnOm5 终qEme|{j}}\QzzMCL^ֳ|m{QzE\&Y~Tzv=;x9=Bџ!_{*i_477_4OQqwi2|{w~\@P:m (4oV@gC) lf+!R l:Ά(+ h_ڗ7о hk/WW`1i? sʭ慴C!ӿ֔'u[!tFdUl?k|Y e @súߍ+t{^e]W}piM j-(f3}zf_!OTIT'QYDe7ﱟD^>d$i$I$HWL{LËHE$ӌ":ar_Dg -Ų6W,,oљLVŲ[1bb2!NHƣh.MJ-+[E2rV7+&[G1bmZVgL`ޮR4.re}nz³-jL~5`1P!?[LgaE} s_qƘbAaBC6=0zd+(֌.l؞cc6] =/i5oLpK b&/c$/6=Ŧ^K<(Û*|e+%6IFd;r[y2\6ƛWU ɶpj(>{V|⿅-|/zh9꡹\\4&gj8Usn~ǘgv-\^g/傼]#m OG% wܤG\U}f~c[՝;l1vE8*#c {-`ױ-0޺~yoxdPo-+9]&P g >k/?|"8p aLGͺ(P3)r~&UTyVzҌ<\\;%pAkO0mޏzLj=`]_=o3cY[U^;ymૅ0!1ƶ.Hg +<4KJm{܏m٢ufIOf^ !˨' 85{\z͚ s{0͵G*[7.O.1)|؛طK 3^+Sdn ~*M|AVcTnJILϬa[T}<)$N%[B;ݟs*qttnۂXqmO3n*{eqW(L0Gx}d㔺_x,'*zXohX,+"|3rW"ILqo\_X{Dh^zV=U9_K!+B!ZR]24[T7 ogƺY_w-<4,ZbqQCv1_c;J_woy 2kU<'qP4M&Q˗Y46;; ʣ6| _\1MGy&{ Ӥv[sHnfm{GU6o) 0 \.=R3s6v.(-@_B<#mlK5@١4Uו 6_Q Pfd<_ò,q 87r6n0ͽ=Oj Onڅk@ZD޷Nm}\ttч.o~S`aPW+0P˚J6ٯŸ(pt!|1F2Gˠ瀟{ԷZ8\)ŃJY#6hNؿ |eM Q:8]ڳxY-gXN-g{BLEo&~ ، 3SrqU8 Q0b5C7/p?\ǿOW5<*VO~p̡s:'gP݈lA9s3h c'|Eߺ{* !1q3^W,S 4!ďk&j?b?cL{5}s?*duО౬k 13Ʋ1gx2kzG\4&Axkq2x`Bo&`~y`nK>eLΙŸ';qO֑!3z>BFyB}yϩs~ lY/i`GgʜչZR9+T*17qub\kaT6kS6- S7W4֗%UlFT)%/~$.;R뭶d i)o7pT\(Β̒:͒1x$cQ7NcSȸʴeت;Zɰ­~A>~B-fA{/fcsceքQOJm=]A^wA8Nަ ӓc?4[I[ٙ㵚-crK6H;}YSG wQ+&X fM0 "1-}I^b}{7(rO?y'cfZ׺e}Feg}D'~ݲ#x<FpX9^̏#X}~w%\kyw>q\Gga>Ny/ty&Irn)rNW*7̪s:y ߨp/UY:{(| }>1sH .ΜCX5tqۖ999ne]Fm)egEղjy_FkE,9'hȅ2IiHh9Ϲ|vB9%I/'SNkIJi-ɒrZK֒>Q>2gqv/{>]@g'Y@}4F {?*ޏ~tPxf9ê=t>ktȷtgnXH딱 Խy͂!Tgvc,_Pf!ɻu{!*lBQ5*vo#7>6WN )|_aڶL,$?B#-$?rՋȏYD~$b+EG,"?"g͓ Ǭy*Q4@plUn1 ûދ3]ha4mzܢ39^*'s;*p4|.0\uԕ>9K|#clZL`sbig_mgZs,c쯽4_xϙnߙ n%\/ag~[v~snl$஌kX3 ?x]c/K^9`yцm,Zgcn X)Ks[,Ɍ/01ʼb Ad0Ƶ# 獌qӌ/Z"嵗1%2vd>DmJ=:+zQӥRf-kݸi2ͦd?;|׏.%YF>e{23kؽ|eԖhTImԖX)[~='E4qw6(gn4Z|%= W̕\4[*?UId_Y9W*.zRJs\#xk+o u/G)|U$Pe?yr.P8O9@Ji0|(a9}?65%*%ߜs,g9}q Ӗ-'^+w.֮b)D@! >b{ybDII(itRH$" " J(93{vrscw~wlݝmzkӮkէ;;%[I85LL|>D!w\egKWƙ_L$*;|N#;hSwbg8Ocf\'Z;C]J.YmW?^9}GsuN54J;ɱia\^Tjjv*kM9iOs3K!!Ik8Ǎƀ!&>/dh_'|v'>{k" f {4˽Cǩ0(?7`|q'9P _$E5*ey53aP +y>'χ؆M{$QVi0t'% up/on9 rW킥y%MϞ_n̹RVeIfncTce>x[UY}7L_fRA%u_#+܊H[̺If]/^BȭQޜ}il=>t=Rp/e'EOOel/b_G62{C)aUUÁ;4>2}5crju$8lVTMr2տdHgU2uK&~LϮd~Lx2?mSBe)Dy0i| BaBa0m~ צVxw?k<ӋSA֭"4ɚ(^kt`%H ߳)f'u(VH)V.*3|I]oq܋Ztar8a7մᩔS)'l'Rj~#cz?lw>X -:I6e#T!N50=Qv}S~uU* \Xm8lK25cl%棩<9mBݗٟk:[gۇl~ z+t}B{uؚ'k}Q SA{?7(#Ex4cˆs9Ʊ\Hdc{8q.L ?HX' ^ b28fnBnoCuN՗F}i|7EM'|4.\Q~BYk vX_ dۦMV$ݜwl8 {xtcBq\CwbuhÍb0%,!\i!ܣ@P8qn~]7"?/q?یfomfԌ{-<6Q6BƲ:oW߬$0ɡz^| ~?~h ~D+/f[ALX+Ù~v+0}N+1ۭ[/cCL?˙>hQG2VǴ*^:fי~@ W3}&_Xa1}=i:C[O3.Gfr3.-8=^cF\xQr𚣿Otw_B&of(3Io_Is[2ݖ%5`줫یEmC-d%=z$8P)C`%b,uV3w]>W#p'C(p N +ӕEc0Աǹ/En 4y#A(e!ǼMy%71(ko>l\tU:P"xhUfQٿ:,*gQٟe)6"g?h#9E:,v7;+;_McLaj6;+ؓ,Ȧp*)zgS8i>$_}+#Y:OX7p lz뢼<+ǜł<al絚>C9$59r W].I"dQV\H x2+ѻ'O4/'\ʯJݙk]\g.S/\5wwNJy&׌g0AZ.ɡDz8t/o@8i6-4W;<x ^ FўϏ3w8Ď31ج}Ne^o~ұ`iMH3.ᨦsk3 y~m=c3˓!"mT'dY&Z!py_5S~NS2vR?M'G뒘y.{< g)X{V0bNƸ'QC#]9۸|Sa kS@]XeWИV`lmp/>ײr0-]9.*py%= RWsl~~uW<5M YZ`̜SS7m;^rm9I]+ڟ~p|6G5Z_wKzBk?~'t߈|*?LoM{i-Tƿ+ڎ9v:R[H}D!R}gծ>QH}u,C*Cr|z|*YZt3norb` *x|Ta,.r_̙ o9~P?@>'d8hZG]5F GpqK=6iԓx2F 864=&܍ /ץ~f܇G1Ą_-'sRl )5Qԃb]O|fM{7[x>9~?Ov14jςbқ:xɼY,!yMAN#^m(v&-C(f+`}8\g:όQ;QL{Qgs`JÐwj%dyg x'{&{g{)»UBS%ܵĥtA.< xkk unܦuI^ZL=򻇻ɽDuv}b ي{_[kΧ}ehexw,~{aae46K8O0}g!1m{p9ܞXݕ^6Ax 0@{qpo>Ք{MV;̙~;8]Wp?Mη^nU$hA.=ikHE2RR^)G </T͜QzzS9OY e{}f_tN yMpP0ݾF˹"׊\\N0^Oyar_msP>&7rk .A4 r=1Q}=[/q50Ӡ0FC܋1)>>X^F1F۹ 1w3|Ry1|;p 1}/L:>smw%c:n~Y<Ϫ@u yFq :Oj;<;R9KMm[ǥ#bAP~{RYKj[OF\I0a,D׼ hNB+5}ֈmN8{n޾kP'{) hu

_\ QnGG~?m)2~MIO)#?")Z[{~APiSB"t D→P>;&#?E| a*g2c,gk/0D8' 7^%`3wNKw@]*ۻ*c)w=\eք^`q8%(XpWE{51Uh-'\,.ӕ_2u,]etE[3aaۺBܦ x3̙: y2}$geDQtP^rw,~!4e>KWʨ>_4G`{BXc:nj_x}53WӹlS_yyAxv546^Cg1Dx/x򉟚VCi oʨ43PCun7?\cԒ<:6=G-cFr]472ͭLsw-p-j-ZVpf1DG8Li6mWe쿙 p !E'E#UH""FQaEpdWT.E0*IH ! G;a="⹮.a N7vTn_&>c8ۈf' #}t0N1d$h.mCyQ50:+;U4>3 70-` Zvi?dCy?Lb<y.2:1O0,FToS~>uT?O}KR K3.ΐteԥvS:boa5^`6ğn=<|_!6m\ԽēgebԽITẈ_d-J}#+S-[ЦNl4n:+uTړU}iF:/7[]7[^j~.%:_)=q|e3FQg'l;#SüLco,ɊL'2}ni8|B nGD/~n_;@/7ɇn &m0c=c"MiyxF"o_PLt10J~TZH_U:5QFo7泅*E24Gv]ޛ%Jύ TxVx¯*]w~G(;ޡ{dI Yj[+mGk^a#}i6e$׎,dɾ,s/Q=60Y4VORSve{nI Oy^> ]̕b%nk0gMjx9 es\Z6o&* l?ə^}T<̧9'B:^\/H}:yW4KFת>}FϦq u{  w,uq,u]2rqxs\qkv'ܭWJ {Lvsa OQX+Ổh"Ϊ-8ʀ> se^4Sf`)BQߋqM^ xC\}\ h/+reف|h_k㓽#u |2^ųg'k1CI;LgSISbw\Mkn۞X}?v`Z vpIgW z;yY}8x wc46'$]Kd%f"oWWy>84lRIܷIL$s~+=qcc]HEwcrrJɘjAoIٔ<:W%Yds1cs'7ImmQ Oif2csDǶ9ܑ?޵nzMuz xĴD3O̓ c&M/hO^><~ ཌྷ-C?}.#Y1y@}( z=5toŲ37:탒_ڑ#t/jS&sp-sd0PN8kq;kOp >2Wo&4&sb5v;:؄>ru mւm0g-qz%x嬣j}jʶsmin(@§sPcz5 eoj h>ނw;5OP hV&4_*z >8?{#DG3nH}BMwg)jA:㿐y36MgE`%JMc3X̼({ӯPzڄ^z :veh%ӯVziBJ*]Hr)9[n͑'Pr2,\ߦS~"oTznb,{qm``y[M=mn}jsQOo|_ ew"Ow/=Epz=Ԙ|Ec8t^%`l֥}J,ϥĊu2@'MM;nM\1;?llO56656>ULu:?ۋch{.bbv]w}3q>qjQPSG.ܳn*j>hi~ PQ3GF?Qo ߫LPw+[)|cSDWxw(|¿Qx*~X OQx(-pYY[!QΐùѬ4N~Z:]!T*lwTRko ;Ra]ආK5,:{1̐w[cwFVR|ZIga%}R`VPe0R[ƽ +zS81RJ gZ5}e,Vl | v9{~ 1@׳JeQCQX.⽉u66l ӷJehWVKbÝ<R%uUT>fqM|f~* ߩp"cW\?H6^dbZR%Xl{ˇLwWVI{ro%1/p}e`;.-Ur{Uwupds>U?*C-Cd׭-xqAޭHɫɪ~<$|Jl1ѡXnQ.fy#ה#گﱖszd}m O# FIMyJ{1*-ۀjWjWtu|5l:}*1ND8%K<~[-PL4:LJ]Ûz{U'fV^:ELߠ۰aebǁj'rڋF:~87}U{ˉC7~ZgO%j߫%V 8k0[/Fdގj |Z(}h+;YbX.}ڰ\.H{kp2,0]Ǫ=2EKV>ɥ|mJӀ~'7<뱖&eOktǩk`}{GĄ11_՛Kzۢg$:i"Q َKj7>F|EGf~=oo <q%쇍OjλGX/߼kCh,bqԢD x 5BJ93eQy)t0k|[WQC6[ZZ>V+7Y{Vrz-rZ)tG9_'yas*-:@3u"je5cv,cIn^c :\~Y% bkkulWf^w"u|C׈:C@xqɒ,, g-wn՛u$c]vII6(_IY6IYίsBQkչDzȋzZ? _$Y}ic뽎mNz^֮K/s5JS<^"]vp3` (*h.Op?(\]X* ‚, *BHBHfBlW_Ly&>@uqی?@utݪto $ސCaˡ3s(9 {~ {8$&=(RfDT6^z٘k6'0q {d< MM /|`b+?} 7n7ߖԃ<?M1s>sz7ExcA}A);yײeq'wv&\_d*{[_T}0%wk;o_;yظgݵ%}=?߿Gy~r x3WPyxq^ޑfٺՃөLw҇Z>U'IQފSz^n}ʦDk8ߚKz娶2|_B+/ʞr֜\_ja/&Wڐ+Km͕}`E? /j3zm"KJkD5J;Dy"[ݢ¹i9{p{.s'z60&S>GȓpReaS<Z{kFş<0FQq\;o^kpq*c}ɐ//d«KCz11y%!luc21n|8t| |ޅ8px壌3`Zc<+7^/w7#l_Җo|[S4ы2U) Oye}pU\'^S-ە {( nmx~S5Ő7ɾ>̘)][nd'$@b8۾倫';du1b9 {tfr+g׹  j1|nX96==OGq9Kv.r9cԺ}dS z~,A{*yz_%T•j?dWϱYb&A|M( 9e&][oc9k7J R+QxŸ)P (<ր Pu(@eQ>SV{{ s?F>QhsAX<]4k"??- :|Ⱦ-;P cdKh^k yč.{J{ >|x{(ͯ~C+(iy60ː.+`r#'$Ul_7N򾮀l=@,N׳FmOI?n{^mxZ0lh?ޅ$QmOy NFHuBsD,%e':2SiZ(b;`]:_pc=iδFZ?O e>B9{119c66Z(0 eoDGڱdeUzZ3'Ǹy`w42㙙dBak]*-9^ B}!".CE4džmw"LǴص7u]@w,9i+S' ^)8qem]t%4&P$mÇxܲ3z?VDKQWOqZHDz|= 1H&""~c.gEL_ ,SxQV $wQSg*y a~Xa8YGX&6!h5IG89lG#{o\G/fTJ?( e@#'7'z=.Y&|W]i0v b9kǻ* 8d3f볍k ~ŲGa+oƦCЕ<Ϡ '~x࿖k:Y9eZv_/;_#O4˾azXۺ+Xo]a9b٫j6юƾVSVs(E.(+`otAz\R'+y3ŋ%`}&)KxOCkp7*Koik#͙y{={F;fïх~E8Lnrt]G(o2P^MB^q/ԆFz1 e(#j׋򾜖܇ѡYUsfd0F41SK6ofv {l~Ɣ:t s5a٦4*W1a=X8s8VQJ?1C2{1_ TVr}Tc%ma g#{dluQodlpL$s D`uQC S;m41}m|Q9y |`)pϷƔ%;1i[m7zzpcowtrCu‰;{DZj`J9&{v})r? ~OH;4nThc8i1l?F&3Jl`]-2/$q*ؘ=ccROq^m伄x(O9.fB7aCB#Ac6Ro!{jF|U5ru3-艔ߟ#ؔcnVZLk'JLi<0c[D3O y]x'!gk ]YrưWpzU7r٣w u&}Q{S&"S7%3yßf)(vo_KX:oiB1/t;g2*.) qYZ7ˎ#D޹kXSe)WD,Ra.Tc+*IҴb,B,@c{nf,f d'j[W| YYX+;<_Dp%߳g_'|p,z»Z-wqU8Ci SAn| VP? U\ށ}bZ&CGmE^6$\I ;reVxX>l' >{\ NM e;7`KP%;m7 SVd# /N*#{Šsef=zc;Eo ca<W:A^w/կul|.㌵~od~i̮~ywnR؆ aZ=C}(W:W؛l- G*z^=i IZ<3#ddȕX5S,CeLòބ}%V'Ƕ7W+=8ٽXi cL01@4l#ᜌ]Uia|ΧЫx^ `:od2Sz%Ƀ]桕ү,%JsJg-hF`Z*[y?3Rޒ&yJ/#W4~ߡd~v ( \SEd{E [ȟWp㕲SEr%U\wWȃs8 v'ʃ%;NJ;6 U*96JօfVɺп(~gpcNtU%I-[U܉iey*4Q8Fa`Tc賂"VZ=ێ1ʳ-)ݬ_Xq ¼>Hc]+?ndfsC[fyv7 y?@oN2*BgezWzQCΖU̠]o~zCq]#jF6Ж1=@} AtHL ʛv_xH{MRU8!Uk^GZMl&i F.Xl'Ό:fJ:>?ipIm'HPo6H{ efoP&tpATs,HPa }0sG ]h.eZh2pqL;ۅBpQ=B~4+Dv&pɹ=D+ᾐ'(vt6惭GCr(}zXHB˺h-w虐sd>c n?W4cT>'|~8C:sN8^*N8x2&T`ƙ?8$}%q?GQÄB=i秮M+~b{i.WYcC0[0[FOB4eϽM[vC}Lt>5-ETs:I+h|jx="n\Ը[O^eh'QEn#ǝV5Ӏ:FcvـjreӳfFTMyq?Lq͐44kxqt~!W_M4.q7-^oek% OT~eO| i*OP)Og 9J؏g~e+{S\C8Γo}~>'uVG_D|ᨵk ^<4FlZ;U7KipD+ 8mi#ͣXc>1{}mZ*0ƹdtb1 Xb/5·b wp 0qps` gd2~g:gҟrNZ~WaUq/xFx158| ﬛#:: -g'uސ DSe=nNi (i#Tq-oJA:EhW?2?&\&Zde "k'sn0BW:zHBU `x V=vL7mW)JsO/UJE?AE/QjEW /T~O+~VQs~@] /SʮqO1GC}lw~nɀg` k2(ǘ3qۭQ] |Ew$$>ANSD].(y" ÕŰ ([D@NAx ".#~/OczWG n{E::%T{ji@u'ـ{-\|06S,|r\@\\'yg盛j0y2eW ~lx%Ops'0Ʈ }=7gඤhGFzN0}-p:3K\]Q "Tp5pHp M߷- zaHH#6zJtCq5qøR$<zNWۅ^R~/@y|ZG1 %O͡\/Os) K({Wg٦w5w=c+/dz2y m41zܮ׬d=F9x; BszT*hhn׃`~pWކa\g5z»ۚsszNtX߿9hl'UlG3Vwϔ61Ѧm_+tp`PSm-̦nv<]^?~iԧѬo$V~Ȳ,nWG¢atp0%}FIEc8ö25me6cxW\mhɗ1ƶh [[k§-[z7AX42"eBuz{eنX EuHϠu%HޛOwKjt=9xX Ϲ+s/e#jw6.#SW^NWwr== t7[[X=-^Y'8]wwtއ֜jH _-%kG,>,>6oM\LCvpil6'8T>h-x,IY2g:-i#NX0 33S0gIs@ӃMDxeAl+}>JnϳLf~/qcyԠ8n7DZ>pLf9Q|%8~:qNL>o:>xK&r$(־ĕVN}<֘;&5Sp /s}7g B^xg<׉xYB,3uV:փѲJhZZ׳e w&z:eI9KG.ǐ. mByŸdAj;}O4ݶD7rQTipweX*2OgS ƶfl!uH|uZ\k~^G=GO/=GjK_U+y((z^Mr(6s-;mGAR/\/y\/mپ.Jd58D)l_'}Hd߆++zgu.2iݪgI>v>>>>Mؠ,Zy=]ן OMMh#zIr,&w gk^)1g@m"6fx>F$C OF 㕫X0ݫ<ɔZ}i#EO|^㾽;x atv,>^k0fyQ n[*5 x+J7/H,LVm$ЌNk&fdyڙd=Iܦ>N6]@2dnSܦJ6uA2˓MtYKۥ_b?uu:*}ExuhۙnGB$'齌{ض/2TEQRkſz׫}7Dc;x3?_Zo ^ku^#X =.:]&:]':*:):}+ԾdSOIoGf7Nї}~M盼&r§*ACG|{j|iE&=mߪϳ$9e-xYr+ZFUO>HƵp]~jMes ^Čs}_W]:!pvF}M!4gyױym~ w%TQ!'ZtFloxo..#32 u`~e2E5eq/(!!F'[^xg lPrIue=^0u*)a2N~=is"EDlg" h2'TNu79ۍYt2g뾹ƟhCY_e }]{_=> h|^CPȺ+3QJϡ_Mygw=xE4}aLcdij3gNd},w}8l_0]yd_dnPi@?q>Ng}t>t$٧LqqEYO+M:OYo ѹ_!k9s jB37gy~QȹgC}J?ѭ{2a,zYhEv"Ci垹kY,{w4cSFGE RIj9dn)q][&3/vUpUh=jjsYGKyl«2>W}UNGGY4a /f-s&ޚ2YB3;q}WzllB1]$t<(얎9G lyttA}zt |uc`}:96z>qty2L? Li) 'Ͽ>v=1 { n+J\PRY07c:^#c{H+>E>xxcWӻ>L'ly=ab=-[mKM{iSy.}tMyJ!o_%Wg8_:lǦL6k~}j6{erCܟkֺ"5>->'-9zmjUaT7ɍa tcn/jᛀsUvWn߶o-C<7qp{Q?Az<z{I3ss XgKV shG- әʵ=?s`@؝y<A}KLa T_Ř@8(奵K>y`Ry)7m|\'Nu2)ynfOwX$y# 8on;r`.8޹[q_IuQ҉i7Qyo-pOиxIg)ྯ%0Uq,01,Cl` ̼j=U+ 8SeLC9" yѲU:{!Zmh9b!{ҋ:$wPWryRwȻ 6r=Ppy-d]b]x]wJrÕdZ+e|xX#r, n)2h]"SE|Ϗ}.WE\'X9s?ʪb+Ŭ/ݹR}1wW|pd χJg~j\i~_ JܵL5W'+1(;p3F/X?LBiO :K'm =jNYD?<4䨩dg&x+?\ѻ5ߧ1 1a~^| n)l)[tOY];HcZNfDW8CZOa <H?śHMk5}q=ϊiA+&7J;7IJCdz\s]L)@_JʣmtO̢}:e/?grdJ+϶&&;͒=ҸJfi+J[iYsS Q}*߀,U|# CٯV1s_7)F@@Wp% UyUNv GWii g~>LOƴ= ]aox^& I$zaa -bg3k=׻sZb#}L= oRuVK]@I]n#ܓ$CIāߒHwK&ݽ[2iCmLlc2}m)œৡ 8꼫N0;sJ|n)'a`s 1g} EGޚL|6Y|_I&~q[9dZKN!OZIR6KwާZ oM1Q"2uz{&&E L|)b_;R'sZd .e="2s%b?};?akS"?ۚB}(67~ ?)c9`L[ZشF#.K ogE= R8ZŷSCSo2Go_FoNEo} c$wOqMr2U2P#7`=JmVZ{)i -11Us49vc\K}@2:q]_ ޿cuF}Z^*_g *Tn/khhob h~blތ;kG|Q1Qv探g y[F '"q 76#C} NN9؆Xc<&5kw9Mtw O:nd:,|.sbyѢsW[{ 㜩0,+PjOnwŏZu$gOJ: 5(VJ}N-jU ͪ&ԌmG1n`e2^FYS 3r);~iY ˦LyÓckXi>D*NN&}Ύ ;FQg*{݁Θ+rU; nw5bEWB銧Խj8%N6* U>ӲI|nʶգsCޙq|`Ȗ3UMrjc׳LG_g/ό Qy;dp]\SF;ǚ9ڑ-Bƨ<5#'iˣehI 8Ju_ssW*!G#| ȇC)KYЏrd]=--9oˡ9oqɡuZGC{sGXt̕\3/ʕ9\rQZi^8gPG7cPQ:YVr=OɐnUkCsV{U%z~voSV;sE:|ySG9eqKS$y;}~[}N{G!fl|Biq<9k;P?R E7{5 i}eCy]P^;0"CslA2tyo鸋y'KM,EBG1bSuoY43œ2\(sic?Vҏ)2JSy}94 Jl,σ1w:yOyU~h~"GH3J3GTOg'wL!OqG糂e6~ϰqNZ9uăQfg=}Mw-e_ wk]"mq KERYN,sSJg\eʾu>Tgcmhawz(|ZK1vG^{j9ZX u[)i~#VK= lylWB^E.6ZY:;:d`J~Y(dϴL>e ]!{\`H>pI ~]jlS)}owb<|^x&ौa|M߸;2G9DX(wg?xFɀe6A_.(d<ß*_~8_7573.EO˺P oF硞nl$l{S< ݛRV|@op2\Osu9]ߝ녾 L7ӹ631901.\T#06}\17#?*cVHgmm͖\uW }qnmҷ7pؿ(Ao(uB{J'dVRJ^&wMՌfy2.Wxa%kU GF{Լh.:9,@,U0g̹/\[,%Y [?1–Uҝ^ \0>\᱉=o`xAu~ !/T?CFwQypm[(džms<ǘH&q3M!c鏭X#ⓍvfB񓪨nj~~uU9UU%{'ڙ&1~Ƈۣ(Fu0vߵUdͧ NcQ~[UZe,u~\RiJl"ӡZl=~b5ٳף^uJ|GV_e?:oKC?FO=x7 *:'V|5_]F'f`:/?x1i]RΨ%9XKrq*XK6mdiIYuc?G;4{*ǮZ #pmtJoq(dk5NHӭp?.n@Go0}Pj t֥QQg-`mb?ãyyrۉ<c] ٛbhkKc/2}I@+>I'B ;B 7v #60>\*B2Pc9 ($nȻqj3v?c\*u|1|1n/3oOՑQ~2ԏqE:k]6Nc2ƶY'~jxOkBM`Mu4*ֱ[h~^Gs&%@|r+hZO͍]ΙͻE[Dܟ,Ʈ_ Mx?P-aznn1.y ૸g 圝Ʋ Ct_ _kH? w.z{*y[QUO7b͓F>XOwƮ;rd~ދd!&2yj03,C- U]yc#S[T>l]H7kc;z^V75YxDFB>C ~pLN3gpZCsRG3 >8a)gKCcAA=(H?G{QꞒ(; C^о}Z/k{~o.U{T McƊ]R|4P_Y<5cGzgykoQU6g?ݡY^ʑ:~=*к!]II@;|Nwk$2{~A`{[YGVp'pQ~1=8eW PVhL-8yVv'tqe-F7^;~]f9s2'X2,9I-6*W+*ލ.sg"F.m~ Gm܉ىU/vw 5\3׵5?Α/^kYmCl7x3"e+.5nx{2Yƻ+]1_bಀ=po'_'g!>Ҙ뚀3=Ect'盽}vgggwgggwNp\g|=8' '|i V`pM\1 8'/:㢀K.ю.{/k3vߌO98E'F6ۂp>|[釲;yH鸏;ȐE7uT8h9\VU6s/J!{𳮶ZMBgZA~d5ILgj?_(:c"dNLщ |;V3_EyQoFPbw#OEPbئA9h2+c9{.:*~$`-oIC&8qkf.X/NL$_ݪH)K= '<*<."ǣ7_z&3*?'>%%FkX*V cyw?Bk/9[4EQb3%uM,J3U o0.iXE<44MePu sQjON D=MNL݅q:fb"D~[L'Jnjw#sQ29(/G΅2P*ZxX9Z%R/QRk $'q4/2y6s6ٜW8C>y]Eq^r'=!^eQ4۟' cmһBP[5{l/iM^yT,0нEר#4ש#4`:O)(#:){ U0vCKzcrİ^ cy 1䗚Xކ1R]Tyڭ!i= % o~ Aѓ 1W#:-1“#1|nhhQ4h#j]HzRr=T.CsEs46 IR/ 7]_sU}Zs^#|='@iS/ J3xfzx9٤͌ | 2[&Wd| .f:8c_|q'|O3 '3㶀3㑀2"cl/+Wxk#ק9H9(G ^gd[i?3v*{/7@(ԀJ7}b h}yZߋk@l h ~w'v+xژ,ì=m!=f}|PC>pƆ !א苠]wT者{xw 8\C|G1 V6xA׺#? ^-X>kj]]PNr[:~ʼJ:Z[~ OGp?lr+K{[ٜw+~Pi9ӤNU]7YoDԣF$sə8I{#I_iL_1i,\oLoL.hLokL\c^nBijBiۄҬфlD§Omm 2 ?h"{'f 5 $ K=w-?L̨U, ?U8>pL7u,B;w8T<^k >׌s4׌s4T5ل~^l!,C?f?Y_E?*G^%MqϹ^R8sBQ›ޢNw)cy3N['򏵞v-ݔ",H,N _ûjT%]5OF~egx> eWؔ`x@#<{̏~KmS'ږs? Zwi{7xrL%=}Jz:7w5MŞyƟ" w=jd,ePIo`:ӘҬ r1SH "؇)m|ʳli@#=N=،=v`E3vjFزP'4vicj26.䳱q! '' D'61:9Gy-T"miFC9L]\v1|wuW%Ơ]VppZJ}aej?J?' S9kY/͜49?!:B:5ߪ[ 'M\;ܳ9O2[a|lP+ )7O>W}gp1_H#{A8\]!d9}EQU\邗"iLQCE 8q؋Rį/T }H 9˱t ٫3ܝP֠Dpz5!<)+쏄}JAV,5X+_x+Yn~+lx`sK>+yCowN1OqeOR紶 POMZ'7} vv1k}JGԻ qO݆.FE^i6.h6*smSm<}OYTFe#6b s#vX;singo3)O2q:/9{G'2P*'0bge>Ru><p[Ӏ&pك95)=wLOLQ2Ն}wQ^ >Kw1V_c;mio{-هeE>"ʶ_~kJ0Kmm$myxղ*z\`Ze>e|֏mVx) my{0/sf)kV[jEԻ;0nzض-I4No+cs<ݎℶJ݅߶ť~˵|?>?-(FM oPEB~;T2mW=z>ѭxxPd?Og*c0dvD;k>[E{||d|ox3N|8Q:*sey{ 8М3x8x/fS/9Y{*c5tPy?fh|( =+T2s\><.k ڋ@gtΨ4)[]?ӡ6 5c.~,eC9}EsYK _TګC~_?6_8elҼpۍXrG|a3LCg u=qv{ >M;/ 6v޼d gĎ9+[xi h }"{if<9q63.t3ov31FhՑʂ~TE;W.Ր17c?X޵<6kT!b# QزGԦ NwUNӼQt,YIN(>zӧ7}'zyػ",qOD'w^*AxPJu'#/#pBz:ܘb9h;,2 :jc?'B΅4HqƶƸ+@y@NrNk8cYl3g|3/1ũ\\A>!~:dzNK39LsAgZC߰Jss3Yμ?ߞc=<3d]r 883BgweWɯePh?yg*|Y%v׵V1FЭGay*pZy#> z}hcպ˳_~S<9~Z\I-/A؝rՅMEl9 pAŒ:O-HfKڮSi~?2] rxB]Ȇe# ջȾlz]?cq."`^@Sʌ1t:5}E lV?pu9^--"}F1_s@LWT0E-Q7Bu]dafQ,>95GI=ݕdvrW]=hW!º=CTc|9=x]E;Pȓ>Iccܜrrއ`fqZ8_YfS0d1^"؊g‡2~ `ƯbKG5qk%c c"hm??$1yc8&pGx`m> h[N..ʼnbP>w"Բt(>w!|,|G s>O܂iz׃~'a|_nLA:,d>{>=d}c_a1g~aaC08{']O|\EI/d} 穽{E @'=r) f7-]OD\ѻqWRcFؖc\p{\gȴE$i4c.xeÇÀ8IǑn^ iHlIҳ)NdX1?=aw ^ 4~ϏؿN&~tզ*>#}"#ǡ> OjGx DCeBhƵn[V$ɳzGb:V}ƨ5PsѬ>r>/ozX=e̘gtO-wko}[tf? s~p~mm Co84r7lXw=㢃:[\}& 8}_ٗiCfװeKYcv|&HU~n?^.?c4%\֜wtЗo4&<'y8\888Eƨd /x`~>`0F`>k~_Bk؏KgKCv[᧡-x\?fsyڣ;I='zO5os?~#ϫq L [o?q?׿M߰^4K$\MgcFY56l'I0磛6}gOF?yKXA䓯5h /zޓТw߾ڞ#=ICkl~XXEޓCN҉{)c{Y_@I7>uzg mC>A=k1.:E5?7todOB{ys4cUt&l`Xzs]9SkwbW!xe2NׇJoқQBКœ@5ȇD^Gc{=sJ*FY5+,ۭw'-c.t{z ?k~ v[ďr3ߤ?@Q孁.qñ5H{ oW.aYJY檲,Ԏv T ~/ ˢ\YKF6ºG_s?i" Me,6ņ 6w"h{gXm,|3 ;9"A"h5zNZg@cIh;HWdLm-=W0蟙`N]9ݣ[ʄ|9 QM YO:; K@>HYw~kscMEn1F-/br9걶 px8e-<'F^-Rs1vOHxLx O!_ǓoȂx*xy8x^xf,z lOdӀ6:BM7 &;{?L:J`f1=,a{=,,:~k\f̰mFؖݞd09S2aBhzH_ Gq}0Omkj|6qr eNi,~셆KQafL Rq+$)p9{p"yM_k*c<1j8m48V>{+_*|'å ^.LQw8!`61N{~m&֣ifYyf ܷ4wL~t8LD=oҦ> gyx=RYK}F1̤>HJHK>"oI}xHa{EJQ_|s[0>>̽y4~m4Un4:pE(ǢO~44ZJI\xAc]+ 112&zibX1F}ly3:w4yeTqn#ښ=oEx[>y}݆8yR}ɬZp7B: RW4<`}َcm(R'툄~<(8 Q pfƯx_ۍ\5H6i#S#RU3LMP0{0?^}hz62eɠ+OPxơd~ndͱ2w w -M@7ѓ[ѓYѓ poNCHOگ eZϘ:5޿lyk ~^ѐ͇mX)s1R;W8UPk̑i:NbMte0Q>(O&o0?m&ƕʻ$2woMܟvkrXh[\,LDc8.c¼̽]FOO6t*qtT1*j[a߄TIbLC<{b,ﻟ*OnNPߘ 9(J>婗!@d|1MsU%[YrU%[`fb6B;Id} 4U*z)z~\W4dhȣh@\MO%wPg24R4rWsZ%ie܅wd'!7r^nt |#e t^ ˢ- a<sWT%~os 5O+soW8vx2FOslR&} *#O*8H_7r%/sc,Ez̽QH7 e}7 'd92&4;Ns'T%wB=߫wfNswi]EUFvtƫi\Iu@1whzj+z*zb=q[ɤ+1w0!=0L~N4MY0 e0WS#gLq*ӢoaoX[& o M-Ni 2V@-+WrMqY/' "fnae:c:g1T1T1 cש0cUx;߁1^g=e=g=c={z16cc$cc}x{wOcc cCxSYxSc`\n>pçH5"wjv)2u6W0z8T'Wue`nL$ǹ/J $^ߟ@<`cfkq>v) p_{ Wr8lLHSo TfidZ;K|; $DlYV}oIV|K$+D̉$+IVJ$H$+$+IV&J$YIL$Y>deC"ʁDs$+IVHV4k7R#@HYo^1&w]DŽpw'Ÿ 9@WF)Ý5|ږy7{4Qtol{*}ww|z)|9)i/oݕ c&Vo3 ȦNi'} A4L&^*H2>q+lD|Tc ZɅirg>O?f/YoĿKcNNӂ?{bN[JO^rܱ;(3)w"=3U.v>c[ ӀpK433O֪AO 9Hr9}`Վg:θ޾Y!e1TC!3@ S'k/#獎36@㕜ozWxc oǙ6qOY)91gf:wLo?qL%SoLY[?/L'{M-mz?5OƫtpJ=%t/oV i>f T}s n.S֥Zͦ^zlҥ&]jlҥN&]٤K=;CT9K5CT9K%!]jҥ~C{.R.\ҥ%]\ҥKwKzùwgGzwywכGzwywOGzyGzyOzwwwO3 ~xI,9'<#sX{5v³<gaxf|xk-x NDhxE9.x_&<,#<_heՁgqxv\Hr;s&<߃Ӌ`. ,}xgix΄ܱ|m~ga\Yoó<#YxV'Y !]VexUwN4gxgU%@,_ՋI,&ye1c<&KH,!yqKH.!y̰OSUdK6o-sN9deRY_->=4'IO@9/a?pGuO.fR񥙻TƧKe|3FRT|O^IJ=\VW@˘N/-sy74/V|A6?+n`dne,LYiobjBƻ52춌"EFK x`N:[qQc9)03oB^:8:o Fߌqow%eÄڹ0v1v1XC)~._/Txcg*k;\^%,`W'QNZߋÑ^a||&9`:q_ $MY2Xdz1Ƴw/0^b;b[| X;&>%iVp/8< p9A9q(VÌ" Ҳ&3Wtorn=[ݵ*>>y+EmgiVIWg]%~T*~U)wRKipnsBwֈ ``V^ ι} qa(rOiq/#><|%5%ȓsyfD+jUcSrcU N/YFI^-2sfAGY=]1s r8!k$='O4юkIЉifYCu R/Fʷ)K)okw zH۔\k3G qXq3^"M12ÀH/(ďkDDЏ:#?k Yg#_bua]siɋ -cyM[wM[-۾@MN?s9Rx16 ?8p.p~`|hЯ80q ئY&>m}m&y^~-)ϱ62c.yv%3ۮҼ+JӚ֞W CYkş$yPAB#FyCq'1wC!{;~񾿛x<8'~8) M&W'!wg?:+?,o x\0,>+8-#0XpYagv?zMFƨDmuRف?bE6LLQ=Qic鸢H=!Ğ=ܷ:1IFu|Bg_wPwUdpqɀϗ ! ,4Vu#̀Aw=5} txvutvE?6+d5(΀_"c2=d=_P\:cu3($90}珢P|u.Q̉A/O<31OG{HF~Pj8dNy}yy}ey}ZHrO?[=:#Ň6lOϰ |PY tL[љ( !F9ppLqs|eREs0O[t$ub utϸ|K>O쿑O%[Le)UW)2x'J' ;'mf('ƿΞJ;e~1<:~n_+l( ݶT`q3YO5zkzWuR.\/u SeAuBf&{ʧgE-" ׋Mn%㊪=|'΀5kM֋zi<)Wܾ8㇎m rĶzd}}c.I|^HߟIk^ໆNC/h]}u$&cGj復qؐFl76ʈf+J_W¯a'c-Gӓ&QݮJ*7Id˾DߓȖg7- d.lom0*o7P^m7P^7P^ 7P^7P^?mnd;)J))i.5n#H87oFZǹ7:km"D8%7<&D87:M&ZYdg&Zǹq$k7^L~v.縬.vE 7o)p7xGPx-e-$w۟.J?ܖ&՞}pGn#/}o~]}\pw| Ea|`r4c[7d-nG6MTv~ͩRIʿS*toSD~tJ 7!gv!;D7CLA>q8 '{]زCmsr.LxN懀q9s|!h' 畖9vRH0~rЯb{ p>YSJZ)Wf #_qXSKɻYT :`.Nڣ/1k==Snw/;yO|bȏt%{m; zʼ4$ӯ}?f]rtx&WE}GsӦm@Ϻl.h<;AiZu7go,r7g]wx6>4ihL{r[,s~V9k4D!P^IeIǫVXs2u:f8n/1itGxJ?Iя4;Rf?tff9|lΚ#ErNP=Yo8'*<pAyU?9vʜ0ʛSr?τl9Crba=$#&!9\pf]Crxg3{IʻR{Yz^ҫ:%˽WKs {i|t/͝o3|e͝+s}4wΣy>;Gs}$Gw9~jd4O2v?@O>S_| ':@>O}@]\>PX>w%!f:FyYWqh|Az' 3 .]Ϝ%Ӹid;Di8DiD4z4&vߤBbWW3D^8?U[Y"۴,N >L~+Hߌ%s9?a{<19` {X%2[|cp]1K>2x"do2F!,8pMƸvيq qsc:whx18 x46v:&Nzo7d~l,˽,?˽:}de1G>˳_檨{ܫ{ J7*}noU,Ux˜#rQ?X~<$(/8Q9%Q9ߣW{fkhg?5o.[A堰~8M|'bXo 9JPxNoWd-qQ~T-<;s. m)I C?BvZc9Jɣ]9JTF+1ꇞ?F}fcz׷1_k1_3:-:ˎz191<.g=u\E䌣lemqyxG,|\tw˺KSlC Q$-cDSN-&>Nq t9N봘@ypXy@] TxޯvNӴyag' ǯh#cp{i s/*o|70R.OqGN`?&H a8;EIҍ maݜ'ݗO8mg\iK\C'~%=WXO)nU׸oj[[Q32g;u差ьQes޽E2^eA-'>ՀoEL9!QT {1~0ȇ v|ч*(򡪔aE 6el]<&8VX;7[ι!#8(20C!_M _M]!_w&6jx|5Dz-H?;(~z_?M|}G#p1Oz~a:ɦ8'Ac{cN!1o~8& YFGi?Hl q&$]ӣ(p+tqMiOtL`0joʕNT BAT.G2RtbF/H9me^kjh+Q`#?G>/ LOwϝrYgQf %lM9L5؁u[J:7y=xf/OOٔP*?e?e?OUݪ}NN餝NN餃NN:sfd֊ZQ*xb?oȊ+Ƿ,guA]%{Hp <Ĺۙ^uNu.WThm8=lb ;%i`$yO{+!ɩq"Nh,q cEx'w#F^Io}ZY^MdN |BӲ$eǜީemi+h1*kL18)3rⴜiڟ>J6r(p3;[9 ƅBp4?Xv_;>R q/H|سnɝx{ܯȭ9?HU^Nu8fL}oiZ[5 }|,&e!:8aY=B-]P}K*\J|dm)hO6^e!G<1ȸY)sxwr=VP4T|9ƜUs+ej6-3}R* u, \CQG9ܤ~N1kOŸκ<|nq2gI,O5R?m ww;,<[ϒs,ΒY;Kv8g2F2&ӜP2%L2'K9&MDd#"hZ,c^Lg 6&y;'`2L;ݑUAw'+i g/osW_9h/69 S6l\qG{Eι4c9n4/8U mkU͘g:ѯ#ƺw^_qL' KVKd  U4b޻d b_ sQ־ }?`o A{rFGtY6!ۆ] 8618 s2cPۭYEqaCWgԥv߮XdKZ "x]a /2F[/͗MGRlƽ=x+Ec1EC'h  ۉO&;*b| Mψ5?ϊu,\:dc h15(lL/йgS^ Zs;65ۑw\2E9nuvYq?lLC2eq@+yWk~AW u'_]s1Ɵ cW2N[s:"w^޻"w^>}/UjOg@J\~U*wTW_1nc ٴ,i71,i7 Hӆeצ0F?2Ӟ증!aNWUyrwn_:5*רeQ#Q]&~9ΜfkRvo(=WwE[E 4k=U+ədӾke=٥Fqy}ooVg5ך:5xFSyU|ǏћP55 ~p.Q'xԌd>meø,!gV'{Bx7w®ܟi ><:({))W_c2uqNM*~Mp/.kɓ;<|Wx|5r|q]#pĊ&]6q:ǔo ޠ~ V` `5ޣny#a@x{.bhwXqiUۨ6T㓱KgR*z͓N*diSI7ИĿQn5={cTm}ov*ifn*KoZ?iߐ?CSj-To:ďPexg]SxӼ EcMM߷ZJU~f~P'8eܐ3yjyȃ9dtsi l 1L}T8CZa$ HrAŠQpŀ(Y1PxMI@$>]3;~̞w{tW]4țwJ3홱\>j+>s0dە5(>ff=XGNOP8t ٌ/?0+)ۺ*Us3;L?XtKGA]!K`\A×(搽!{Q!t ԰C^XCo3]ѺCOՊCٛ׃ )z 2Ǽ=q}S/<Pu*WYClk3t mNˋ725B'|BL"8YX*o&zU7b_~Y#!Nؽ'oy:u c.o7ODST2(s {gҝVT әQK=#3Tu,(0ob|P_<N|KSt)8Dw!8_KIwMJ{Nص'9_Jp?fO yH0ÂݵXUa=V(˵v?ޟ|@_X@ qʝ{$t_BXo]e3x=ęꨌ—]sZ ?f|| e`.*mgMߵD_]h:c'e:g}ЉO|iWQpe/dy=pHӴ?I ^D08w) 9u C*D_PmǍ#@MYW*Ω#p?zՎ¯'zy7$=:É>FsǛΤx+rgoszJ_CD,'E" |an '3k&+uٯ 7`{o(2W;྘Dx*e3kMhy?;zGH%3W0ʧC)5HL8s|f-峓+ciiHS!ߟߒM2XA4#yy=cH± [q<Cp/:{ :%q'^^`Ɏ]_ mnW2>UWn~wޜSݴuvQ!;N#6kҬCo%Fd^QKSy~~VHWw(u`*wgZNVJş_KwLJA{x 7T>Vt&ģ~ (R2 J{3X0SRC69޻ ˄#L'r]&ã>7ktUz׼g3k~CLD򋟳htzF16)Xj4Xйi5U">`G~I >d88ܹ6~7E2Lg7tDIwJw9^f/_|; wO;"/٫K Rs[lsgv1̟ji(2Xw(T6>T>?o"Sʸ :I {1>N.X~ H4\n=iz0+qEyNr:"%ag!qtE46*Nç=/Z9kdn_\"7.;s%N`IyEtTp`ٔibM[pϕfik߫oUC-Cf>a| ,\=MNq6W%:F8SpV)6\/N2F֑nkd/j=gHo- wN! ky7d[ư|~)-Ex12HI#K®=k3?& )L߃O p߲lap<}l|һq~4o>4힎uqsN?9]ɾmndH^ՅuP8/@T Ք>OV!C!GG')ղdF{t-R :S o/pLt#Guyaۣ|pMX?P~7F >'4΢n\l'xy6 |@IxO$r9ʉ%r:,ׯNtgdۇz"n8'3oHtK,}TrO>` _,Y_mr'̹*mw7Md+ }:/,z#Gjx{?$p/eOA~\l'n¿a䙗;.bۇ?ZV..B:r%f-`aS(QV =l]p2 pzp^Ǒ݄WK}w?:~wK >HxX:ӐpRuYUSKgߚ<~r.<&y7\ N8)(8ū`IU*ԭAjA.A8 CS}O FMc"\{AdI8~?`A_wl M 8INIZŤ!핬eXNgFETQV>elbs"Jr77 '|DuO}W-nʗg3X3"/WDASzk;72}]>v_adQvLxZ;0'{1 3w\(-g-N(bu}d!5EdNOd\'|=FE8_ N"SpY.9EX'eC =xpDA=jK P$୓,^~q@hOI8> 脢π$eTZQG*dsSOwAsdsGf˦WFc߿(mFF+⽽Z篔>V(pvQ.~LBUѯVksKп7 L{G0E0֐we}6=1Rٛ`[OQr' ~ybCE| X1sb,Wcyi[,>>s,WL@zģ$1ij5dzEËqٖU޶`^Tj0&>4O[+J SNV ۫ GoptPuR-pW!{%R?ק,o?bVrJ gY:I_|^t2+sG)>2C8OoziWh{? NWf͢wCdk?/Bn,(7fFL9(ge1/Ixk3=~LO7hn?ܙxfGY .S}~Ƒ(2A3^ Sxi!UAqkM̂%;]-Ӱw(s%xׯF`[I>E[mcN˵8gh|r{L?9Ѫ`uFOfTxE'yIh^|%6;;䝳NbRkRIL[7iLbھIV.{6[6w6Z#XY(gK%%-IIlz"mZ/' $a}6_ܵV\w. 1U'b zb\3XOOl?_H+y3=q#DxZ9[1z6az:Ē' ;Z;z"~zN4 ǧKr^_-y$~/y=WǍ~W==p"=J8Jq9>Qe+b>&sz9\Ó%sYLf=ѯ_'ds n)\ϗpZeR8N ,ˠm A.RA)SV 4!y<- )ۭ)۝)۽ħY&]iI_Ӻ4UEKs9>퍠'TI/v鉾r=v~lz>hpTww(.BVs aLRqݯ(]#ؑzNwS=8{ In&p~h^䳍o^(U}7<3εu8eWSqe_`Vk*)kY諆Q 4 ͍Bih GKaZ;tcl~ xwߏUo}֞3=kSf$mpI ̻:zO"Xoh>.4[-G+]aUV  >ǗueR,}#+UVvłS $gW)*;D#Fh\3$N‡_uBTy,*=MŭoFz/ x'_'»ŎB=dN¿"C03|2}l<{9zW\%<_],HSwVt{0=Ns_@wMPÿ<#; 4Ɣr,˗cz9-q=k {Yuksfs /R.}39Dv[wg!+3iyie'jG6DsG}XFEg/+|pp{iUF`?~>K GNQ~"N6N,϶t?ֈFQ~6nT0_;U]n>27(ϺM("azYgbS>u2{I­ʻ{\cbl.MMy?KpԭI|7Wpe`ψDwOwPzLi *o wOezs  2{?v}a߈`ytF@:Td v"k$9 m+<**ʞ=cѿ"G!%xK",b^*\Ipv6"C5k[i+Fޞl\& [uctĵ4u\VWVe8HRڄh=yq6VVE|Y_@E29XɵĂGs,>Km?Sy2w"UdbE5w홻:̝h{i읢8OfgAmSE|eڽ6^#y#cX7oQHtw6U1U/Q !lZG%P%nup%*q|ZݕX>ʼWs2Z6my];1ul{:o=s}`ʶllۘ 9v;SﶌA.x8|b鶡w+VkoHv cͱ,mE41e=5*vDtee JI9,PT9)^aēWיp6ˬ< 9,#%2 ~2gm1.ΖϤ>58ћwW=w o@װ`Ȳ[+|wUUU83p?P*O$ $ U$Zk%ՈQ#M]%i=Ӝc~o'ͽ7:5C%a=8cUzC6,ծccVO$cXGIBc*O}QI(<2,2hNO{Fl|\pnj () E(e8)JN]"ϩLFFI9OW yJMR>UR38‡R8b|LN(Gj82JWc7=9 μ{ >ǟ?qD~dVdp- qcn-WK^g4jVm6#_-fq/,Q{N2G]Uk&Rk Co!$D4BBSF\_o UmS8rtƈ8o qnyF|kMrR 9*Gg^@5!b>K޽*wG%%ĵېs*ѱxNF5eWئ8jxs55>XD{߯:7Ľ9_ׯu~ݢ:!k#Zbu^K\Xe-$Uӹ:)6ғsΛyq{uvk~Yj:x޲#Yҳ=ѳ.=o'V7u|^p:$Oz?F hp+b_݀|i >qh>د_`ۭk<7;c0CZ>sVe!Իgy@ۋ'vN?yb;|ܶuODB=9EV=Oa Eq6>k{ F_F{&A\5QYv<0|v1F__0y[&(C0߄{}aGkض~')(XίW~Z{9-M3qܶvEC,֟"Pq#;k&x2syI37mveve|@XRi}&*_/F9om >G9|eC5~=~3,Uqj݊aEl}n(͊C»B6$0o+2|w NW~#>ϗCOM{sO;CxXhxn] tǐ sL @bjR'#K@з`i݀0J?App{3C![~ڂ(~+~7}|U'M_  9age,I-vm*1 m!-8ձm<6׋Qiw5C:?sQQ)cK,t2L0ќ q9$4d<`Ch]G;&tz_~~BSG 1i!o/w>|˪<_ qY$⌢ cpO\wjE"2pkH1eh͇,`ty_iENԝp[3QS?z/?n٢k,M0"+oi&vm +!ԫ呆-NeŽo 5v"75 xJ5ߦLeZ6Wqߜ9<stk45\6f{Y`aĻ?(7'>Y7YeYL '؞#yMx"#*):E:l^ބˣQk6־MN"|LBLˎEeQ&cX_Zq+(OːS}$g>lX}k2Ԅuʿ`ՂrM>X}\*Рcx)&5 W̲kBso= 8 ˂C0U7)Zr&vnI՝& PE$<ʂMin!TuP Ӕe&6 njҚ5Mſ&02 x/qY<ʲge9a)Y^YywkDNAkMy e8lu~bF?3Iq7<ⓨƺ)雧@>J^L#2 -d%}Paayr0zÏ)NlʺZ hwqJwt}o^]{bQ6FAWOA7 ;vojjvf0ֻW%i7KԂQ~} o.⎩Y̻by'מ<nc_My m }jf fEHWot_աVY﮻S]L5[Cf!m>cT_ɼ ~s3^𻄻fq]l3޼KIf2NJ3 ?)WTdO5 G#h-%c@)sǵೣhZ[8Ue5wT kD_[*nq$'zudߦD'}ǴR&hKO ܂3"ʚ;09^8{O6vRIQ9KasK"_{~xEmaofc6HjUi R{=H@ECnOviEɇWKGH;- & F<w6j)[IKi} ې|+9ߊ9kd0ed0ʓc'> x=֛7exm{^H+;w8c6%eD4mv22':RL+|3}wSmIVw\o:`UpT~?@0-c7WtA5͋$f ] yyGs(2o}ይ;]_Iicebma{~+%+U0ת::6yު_Chc6\+Ȟ6v=.ɠvFՏ$s>5\U^K6vmyk6w6>d[UAx_0@1{7uN+|#U'dx=y1-U]wmkG|uX mݱlC|p/MYwZY[F[]97>][nim9_Xc' ],'2ՎW/uS\w?;}cgmM$sr;;VkgXUڱ !\h.dE߯bv^! |Ro'2-ȋ=῅YY * W~J}zO; 3/z}ə!T?Ykޖeg5ҬZ/͎llfd4ۏ ]~Ԍ* ]x=[C߃u4kL.C'ځ iY Y}C?޿۾wx4nO3\Η\n7ڧ7U3͙4+G5>ݿZ1V_îqܶqGV 3'm[yIҧWo/w iNϡ|n _'a鄯6Myu{w?2v~@xD{9!@𻄗 ~0l[?  ZG|bx{N|0^v /Ҕ4;1MV';?[&at3]k{LmyN|O>|Sss';6Y9sN9w$!CN/mgR>o(UvəNܷ:XxVl?L3WE:{6_rx %;r8Uu!rBߌ ƱznJ9&'Էf)aJL@=uOߒVψ@se ـ9?"?p-χK3;[_S:sܙpCgw;s8:/ jta_;8KӋa30g=Փ7AfNb,"Ava}߅;ՅKkx%Jc̅1s!wPav쑎fG(yWr]$;m"U%mwLL]5w@dwZD}Vm3 y>}#iQx^xp%J|J;^~p%+Or9:\Y2m'+ڡ]nrrLusmWn;te}➮l~+&Y^9x{n0WbC`_!7Cs@t/zl u _Ef8Oc0?w!)RnqC*o+?+SC݄Ku5< uqyԜs72RbkmBnvn6QlYlV{J cxxMxE7^dzc 9֖ ~xٵ2wUO/-#I {*}Vd{)[۽(4ィ* n|f> >`Qz{ߪJ8xݎm'l-/zxBP5Y=(Ռυ=ͺ5li$H閸WJ*HبS&˅'i=*|Ew 񹕽Q ȻoG[}1}')>.p >L>ӳKH"/%K3 V> m⸽VRu:o^8;b*=籜 c#M^ScӪI=x4%h6i~|̶D}sRCԉ OaKzn#,V6< xXA%,h ˆ(Z^ĺ,H!G?q|Cxm=[yѯ=\.g"=Y'٠']{ѓ=YYړ=Yy'=Yѓ}=ɞNNc) IX=#_[~2/'QkRx\Fk o+ &eKF9:p)ń;JNsv\1X[:]%>" iٜ櫬>KZFW}˧dF#~=\ F}Px93b@I<1zoF᱅⫉ͣЂ/{+I݌Fw6 ];^cxR]cjXN+ u1ᾧ1GsT/^%x3'Cd^spa1N us w>'\e\:޽v0C8ۿWe4r^,b0˅yX.gG*]-g5> g9Ы`Ntop!-[[»HW#|vk/ o97\f 0x[;1?b]_ձxgq <5I?zvv= B㶃+{s;`nokv[mY.S􇩼IP vНV 9c>I'*Cع!ywؘ$ & W{ycޚS3@uUG*

vL&r%sy&̼eոSOk=y-v~oNk|9xPXA<]ijO j<6&~ĉ>n>֑O޼@s&Tj^muD9{yݜB;*&NQmfj&,Dϟ,q,0B?K3Ly=ww(ytJ\K: U:A:g KktK/oV|*pY OPxX!gc߀R~O+s wet_> Sm i}O]/6̍F7b5p2u}N7~#zJ}I}~v&ߙ}e}y⼙pe{#TWIgN:]Fo'|؛3m7{fng}^e۴]X~.c"Rd$qKl+:26ιpnS}`w*|7+|hub3" vǿ~c [~RrE?_t֤ Ä[Æ/g.3ӯqgCv.ͷćν=N|U{!rm@ZH3OYkf/|\&ln.93s`غ1Y£@>2nq <|;'=y.6|$ >$/Իyd>k 6nO~gs6̐_WO *Jk/"-9W1O}kEo~n?Y~ #[uC}%>hvIJRe& I恼n0hJetB!_ f9|VM:_9Y>}ơMoh2Us{G᲻? k).*^+|!qQ3eqrz7& _{9LJ !W M&kgp?4lHk.>/*hgn M۸{wٽ/s k,!a05F_<⥔ԙ"*i;!7#{˳-.u`9/lbZ6* 7K˫z{a)FoB-;.C2Ƭkc>_W֑VyIm:Ƹ44FzCmxP]KZxi{w7u%UYZ}G^]3:xNjq5Ra(ɐ4r*&zg95rOVzḦBј=mfƪ+L&*a#u^-}ߡl0y7e;s-ƴ29lw}!e"zۡuWv N ?t ZƾyyHAmrz›i{oYNwAx7_wWeb"%E}fP1|8P5HiwO 7C )_SbuiÇ񾁙x?Mx?ѣx?0Ott'*:wu.w;练 g_w g_O 练ξnw 练Ngy;?s4vtϯ׌`/ໃv#'H;H;hH^ӟ3s$a$g~4;k$`UwPtNƶvNJ!א>=x[v˰oA}H>#2}\f_tQ<: ̦w^O2}*",H$ŽJH3w/&k񙹳7g;c #fccM~ʳQ+Ki>ʞ-@_u+8h u][I>sCC0gF܆q2 @9f+9cbF@?aa{YRf7L)|ĨNWdXk$2x>j=NaKs3Q2n395,+dYJ&ȭL|2L-3_t'S3dC,;8A!37wNS_xE#}o Lo˯#$A^06$g o|!k:! eZ#'df ~o:W!;Gkھs[Ф{Ql+-xuTy>JDT`Q2 mPL͙oR7#~U^!ZIz+|KQ23s*UFemf>zߠY̳qe:'?1DX,{O1Y'fqS=ayڒ/4uektζ~b# v/@rMN;-kTYK-KZ]'u]#,>Ss+ ޺޿}A?'f/My}gb-edz~ϟo]) dsSy[/8"uH[8t\GT0 ?x`ܦeylsYfߘ} *;ol{& Gym 8ㄷHf,Ⱥ{FmO)r\}:w͘^϶L?J9B<>K oͽy60_a;2W#2+W8^s9/e9,+0ǝ'ʣʣM"b7 e_=RiM͜'c>T8}<}#^)'GB&W|9PEsbቔf:0{?j5 ziuro|S.M:Ak, ȵH/\vLr ?%QgE<#(`\KqgjvWtm{bsDJiG r쯚kHtsOc4@UPwOpC9 <$?`kfNLvTvOo6`kF)xJ޽z›~V ?j\2vW$Lۺߧ'sy,F>ݭ7ΝC.Yb oJ}ɻ%[#8  8mx{-}{Lg?4I]{kW8C$/8q{O}z( GuF"-i$F};07!&.q]s >IM~L;`C'ӓw>{ X1FGk0K3ف o7۾ᕉicBAi}&qcMg*\1Ϧ_y4,G袉N?aʞT/!:c;a8k0]^WnC12~E06NNɾ1r> Ǚc^nzrPpiV~W{, Gt/:F3V wʀᆞx?!Ʀ` p"%tsnIv !L}ձgwn߀\ggBSr܏p8 Cv O]~\q|5}5R׋ݱKH1}\8'+PZ鄇=:! ^Fmi/y;"+|7Pt7{cb(|eK˽+Nǻ,<7s;x?gә ~h{L`y./*xrc`c3z 8ݻ}gNwkzKh?ڝB{Xh/ >m~GNfLnq?zz"ĵCE:3dm`'q &oI[kǙ3 ]B~:$vs˵u[N?{ߎgSM2<1]sp' nƀɌgqEf!|P悷Lw$w*ˑq>79ò ڙ $|*uAf F޻Xx|r`>r%'g;z&?"<z5EsVg Aߔ{g≤.aEOCxG;x^l O1ٞ _s uBmߍ{O ¯E^B=9^IwGpk:q) QSOVwz{̔{{{)v `[֓nyVWNvy)φ)v ) Sy7 }ށs/  3¶`$<1k3A{'e=7~4a,o*>cZgɡ;v7Q>FS%' MҕR$:TQ{;^>%GX[M֟L{)Ϯs*\W⍯G'}*qF8K@i.7LpѾ6OwtOM"I6<1z9[7- D0a y0D `[gl͇A`"Lam+]6  nBͰiMRBq?w"1Î߇,$.}7׈>03lvt2KCqҖ|Hx_K7[rt|yun&/Aol&_w!'-3\*~vgau?k:廆O6ޫOMJwIw,>OanNGfٳԅ˫DSWڀ|\mveޙU\H7=pn4g[ "hzΎޞG̶0veh$Dߙש΋?>ێ%l;_~a6Af=[W*1q;_au9׳s|"4 ~9/%jrz9s:<`N24׮Οk̙AcyN3M3ɞ8!)Sw x%:0fg~i|Jip) V© WRUnpk(|\Z \~\nt "gھ[S/8)̳2MY:KW4پ\+S{o(|" Uy[ːVDXW>}g0i P6WxƒxxRn xkW:vl:N-g9G eK TJxyDZy**-c)d_T6QF=׏8kYSC(nk$|-}KϷ'݊1ͷǼ0}r||lWwˇp7/S㉮|n-3xoWF~;sy~3S; ]&*>'zunM`B|;Ln鶞1^9{>g@9Qف}o;wZֶn>˭9Q3IXUt҂}-Gc\>"~fjXvL\&?MZ:.{eGZ=sܵQ 9N}ݐoo7`eЧ_) .46=0] Y}Ke[yQeַ_l7>:߾<{<_)xJ+Dxƻ] XuLK8o{#ы#QFg4ƕ!y|]nA5שTksEɎ@@/Vx ׸&8se#}k[b- 8z85lC^.qQTjij$ +Ham ߪ ߡJšODž|{Pc2ƛu_ )xXMG$.Zjd?> }M :sxIk Zh}D`MNO Rx ֜gׁ̚_=A.tX\ y>E<,xf={.g"._+=שE|+n1-9^v'%φRbj?nϳ"3UM*~Fų:اt:X}#bUq^_4zcԲDNZrK8|خso"Ưu\̝y6~6H9]JL w/xj9:9cy\ˤiI Wyݭ ]b ,>uKy}q%VGwc2!hLTmU7+%ZZ"O[bj-|yw^g>opYh]}//gjt&=O_+\6rOGXrz>G$wQ:}++\k)oSO1w۞Aٚ>Ԯ̓am,T~C1v c] qo%%ƇtK^< gxYJ*ă6x# x*Re* K͙S&"ڡ)Oe;v_͵^]c·󀮲woBAzT XFTQa`ĊXnl>u\(Jb &B &"v"X@Qq,D9s8`sIa-zYr|w}>꧷,#٪d>zR[.;/yhl_ۜmKeq{P]sT퓣z 9rTzCQ=trSsT-Q=!G>G6IԏyOLAJod\k}Nոq=/W:!WzU5q+W:7WZny V'tw1i4t:3e/7iVmT\|60/Eހ7ݔ<߶s 橝bj<_F,u32{zݷ{r8׈ļ4Ax<:)r$O{\ ^e<-G/g#sSmdUHp}M7 4;NE}<yd^#Myad9/}{E3 <IkkujpLM࡝>׃^mJ4) |O/?+5JU*?"?$88G)pg>g׈?'ׂo%?k~7'ooK_ >bmɀ"H~_ex<@0_6T~cꙿs|{rE}/_y̷}Gi m9s_IXx\Xn&1Y0iO-`y@^=x 9 |59{S 2\w6Cmie-i9DQg=/o!g?Mv2 'xMb-YeZM&Eױ^mul_ > 8m'7y3*;ț3[%`9 -^HNQ& 9#ulQ7L,.:7Js8$Y:8IT[3~QKH=JGA/ly02?ߍ%Uܗ9gB-d='zy?jZ]|=x:p>FpYIҲ soeu$x>%S(4ɲv!qN.\"ͻ.E-R}xp(^Pکˊt%˚1cFZd}6D_/z:Af=zO0ڞ,'[%Evxm>W&x6tWD[y \!aϵI"(cjs[w,ֱ/x Y[]d9ȃ>N x"@ uxpXia+C7`~Oc~o@`~siG E< qe \_ kzGN_cagRgPzy_;!z{!=F =DKH#u׸u#絑Y:FN~@vדM`͎{ǨÉ{'0&F[sJ Oo[Cb釈&y~Ac7+lD3"F~ߍ7^#Qϋ.?R/N*:|kSK?-նTť>FKu xg?{53L9->Nq|ˬ-#ˬ-2.G3ʸ>hm2:>WyJ=[k_œل? ,''MN[w7ׄT G q7NהžՍ{kS]ݵ/ evO/ɏ{w_tVgO:,]%=o1ג5:mX{='Qnkp'߿| !%oܽZ`hZm xo=8ଙ/愫Kp8-eB^w͑E__m0 $i|53صϢ,46.оfӵBkZ}ąܿPxB}fm*SgOo;YOȢ_VOda96myx0YtɳZrm#Gv. 2嶭9?{fZ&ɃYN9KG3f+,W;-{o3nYӼh^tNEc?H :OyMO)X^ِrΑ0yCYBřZ/ThB \zEgVh=_k6BüBw+tm ]BiIR{%WgV1]]{&W Jms}+տJ gC=56!:Qǐ]?_[in xW9/*f/cPu|TQUƧ_kK:Uh~`ebGw'TaH$w'OddKJsv3uױƓ"4}ҙ>&K\bGؤ{{HOGc2}D>IIC>bĤ01#6LLu>Mvh@Gm\U\l۶5Oyi^4Ҷ{ҶѴmGt>]s~:J}7Yf/Vjsb[v,>dVԂ No_zO=uAvjwJj jj viMvy5VV[uv|Nǜ8n5cij͙J~2[ƙgI~g-vdvdN~$kGi_h#*lmT_Tk.rC΋my|}/wպTzu(:ŧYܗLg׌)F4!GfSN$ EY ?&;#-n{##lKc˔82oAfݹ}­2n kڬ^&nu'~۩n6έst'i1n m#[H]OȘ;S]dYn5t_y=6Wc ɾ)6yהg)%5_Y{$5*As=P!}ȳ(ALa&[A=dszBqۮm3q3>/zoi~}Zm7u 2^%e%ڿn]oY_aBe,#΍K\/7pZ7y|.#3<8ȃ <#"#^ik|t1w/c]桙'4e9) /}k3m:8I\r;#!;^ txwA\Sz.N y7)u/9YzYH^%w3=ݳex e!|/sGW,̟Ծ3'*W۳mw;8n3+Ǿø#n\o- 5hү׸)6X7e(bӸ9A7dL$ys_>s1^`fAnt/ej7{<iAʌ}uc9Ά|w

"1+t`s0Wgxc/ZA CQ;Qwe@4 mwG5껿|n|5qXh E1W\}=+u¼6 d9,9,!~eOd{JzcOɲLrbI/q%K$+i jb=nW{!ImUnN[=ȷSȷ'o&OAF< w'O$/{S%<| Gg;ϧ 3%RgrZ+͝Ý\ڴy r+HL2I&CEEuOcyhbȕe֌(;{4Q wDNg[lgY^x7!hwe^v KZ~# ^3VV ]xF ~!A}]AaAA !/$xd"sHycD";1Gϓ`uS"lTq0f(D}`[Dݞ'|,QxyYmF'7m<[n[i>Z0$3:$iZoܿ}gXW f^>By]5쯑($ w ؏RĞLҶɗ$~&}'i{$KA0#6|8cm)7EvIreb^0`W\6e[osNxVYkt}Cu*}NSkXG5鵠N/m/4멭yЪ(yH6q;O:Qi{۔dI}g'vǑL2'2u)Yo L ֗%[mxUẏ^ v_ޫUNy=*Kxk}J(=YwWbs)<,Y!jy#/ād:J>q'b®CFs DRG܌l`}_9&}t7RKcÔ箚"b[؟fJ;x'ES;v]͓p8'f>ofNjIF.Y9R8SDz(~l4^^rE Ӌ ro9e`'xd ޕ_i+pR|49;;KO\m|aJE={?۶kr:d)ItR؛]Xߕc}q0~?ׯN}wZ Q8BZ~Z3)˽ ؉E(C!a5P|O!Ev嶈MZv\qn pK}qƳ> )dr|׻콈+k^Wj y7?+eL{,.ozzN|-3L=#)~h\ yoDqzug)i>B?aYrHHmٍgu*3[0KSadi,ͪ,,/'b.;A+1k}^pZˤ]Cװ-7y_U+Dѭj(OAȣcPiHZ\k8c,7|H.<d=_90(Ӽ-6m_ͳ>3Th\sf[Ƙ:B!ψkX5Sqqqq-:P57EX3w3w]Nxk'h(zyddJ K:遝q§u_̋}N M?3=Ɋf MO(aײ' zQ!! 3=^LOP혞_ gzswbzOL].9o Z,: ;X$^mW!''*!ܛ O0 8}§ ϡ.+v[p|@3=a/azZNx+$ac@FZRi'+^lfc]D Z Yд՟hL{wjNǿЊL`c|e629gh/߭]k|z9m6v ]#H7h6(#Gaȣ7u< :nmew'unmM~3}Y N\mЫyk6?h^m}ƸUŎ!?ߑ&z3ڠWocŶՖKVC/Xlў{`;/ {`k߃5< 5{ka}iwbiN:q}))u Ou+1 o*%υY[G]q@}ƊZCc#3=uCc*N 6Y;畸YX ^RҕOqkJ9S6%z]~gW0MY]k(D>bA߃Wڐg% Fkafzi Y\6ȳ; le3eRr/G*rr}|9>m_WmV]]]z@^V4مJK)2~>`;쏺,.=9JQ!k0ϯ]M7΢~C.ƱwҮDntY˼XQr:Tkkmʋ𷂛x/`fVՇ[]X+z/]ֿCgO^^ \Lx^9D gn"|&Ox3ܽh7^>Jxy 컊l=B?.请 y_YBQ׶Kr;߷ǁ%|C\}!>N{ѿ=%}+.'u!iEOEFu掠oR7g3/ ʀ7Aɤ\%7vȞ\Fg$ ̿}rU|v_F{_I۶p=7izA9ck4uf[] Q߱62xN1(kabPXg̲ZtB^_ ATK yime^F7j03s,}EZ4M!a{F+PσUlh|^m>.5~iGO;9<כ&gbR\/=-ipsGWpo>giD'rߝT=.e}ܧ!L3+o-o$L6)SeBw_gg&앻2a6d݀Wg^Y {e+. ;9a)/ / {0was@~~sY &tJ_\& |H) |Ma]# 6 MBfg|Oe|Y(ߦ,,_1 % ς=֗-ٰ^r͆=vj6OfZ6۲a<sٰ4w]96:{ߦ _8ע(Qr]PdmEHy|2I{o׉z"Y퐷O0IyXF> avraf+8;>'|V^Cзv|Bh:C'9>N8I+Se'|^&<0-j, ܍R#<tU/ W CN[]{X}=~ $^MDñtB\:ߘ2oO},S)wO9ћv##{Th0@+3#ySra~9Q<|OvѬ+tչ|tޥTmnqO.g.{丵f:Kqs+Dh>bS/'.$k|a>'pMn '}؄W,cU\x@8Y0^׆pv"vN7?+_6R=fo;zǴي' s,9LvVV<ԩ~6DIL߹ь[Z^ ]*cfVދ|8 TLL8_Ťc䤉s2 OA¿H\h=[d]''`C6r4k':JSeBy!Sưbѵ(vn)^$$ wbmG <^ARyLN0۶o.?5JwLO̾ol/,A>9I%cHx`^_|'KgQI rܗ{'jƑCDc>1۴e{vk>\_nyΑm /e<:r+y*^ynTy>}j>{Դs{˲/R\:s/]ݣ[):LRǣRRK{.ץCGsK֖"]=]Y ݩ̞Ѣix?kGt95j*b-{ &]ijڥ#u#}u܎+{& >I獙'hv^OWU46gT>הYxɻۈfʰi+miN+W9,+ǷWX9t*Wvo 軥*D;ƒ/pSo\B|qWwg`#CB6oߡJ;_DOe3$+,z2rRiY55(<$)6@3eŕq+fzJ!4+X[~~R׏X-&Q w!<<Љ4O?i?_2T<ҟ=!OسnH86^YL^x[#+42g #s[ s<ͱįopP0qbmzh5xXxs蕻۹/(Qe~>xҳNșg'̳*ȗ _FWA̩,;TN tƺq} rd ,s0Oh(||)ǘ~ҟ^=8]>&wN_ǚ;AsWW>? 9v_VL4*?VFg=w;شt=\Scb9to+iMX5= e4,; Ji͈lj'+|w)<+lz:g_ !sF&hNP4 Bc7Epe4)3J<26t8%LYq:[x.Q8H/5b VwYZ9Lr r5%Rzkl1kMfVO(P%5o93Qx+g&y̿Ql_^*/tK\>GΉW޼=_VW&|9ٌsȶa7 pcM <?@tAx O:9%Ng+R0'#y]q~'ou "gx1s^'^BYxaA< E.kBƏl%.=q?|Er };y]"4{"wVk KFԶ܃m'f|1{O()'ῄZܽØrbw0oז&Ǯ[DNCNЭG] Ωm"+Hn=62n5lDU #6pW^!z^L/S nG~u/KʒRު.ӾLz2S̯.Z=t/!C. ̵Ì?%NYq۹8!Cp)˟+O"cZ >7rY]0LI{L*}‚Ǩ3[KeJd `E3R;x:4{Ѻ zYͽ&?ްͳӻ`ූ5 v?= mWti} 2 [ߚH?flI{(fg?Lx^۾%/1"qOu-6;NwVc&q9cİ2pWǎy2*p@#kY,_(yf;vyT;F˼2 gC؎Mȧg^(k}pCOc5Zf;)/uOLS)_dDFȽKFȊ;!+f5BV<YVtgug:Ӝ{칠\;?ϙߒGJzl4wdwp ox sz-ϔR6_m[ֶZ)0qETMvOzBgͶ74.sef{>} y`l;f֡tÛX"Y'&d&mL UumHSz0}yrǚ LYwBqlʙw_S&Z60UnG#7x1ϺeE+b9$6++R;Լ%_؋k _t9,=vk$?}nƽH,k/ú2>'ljI$4>9[bMZe{q=jmy[ O)y~kv֧2vǩnXϼf)Ŧ)iܾivW:'QFs=& 4( Tx*+aU^) Q ?NWxޢ UzMsy$n*% '*\V'+FRj^¯S(Oި RE{)0y(Nofi}G<8Jf'iT{ы4xH/ҘEx`č_ya#_h@x UWȻ NCO/ikci^5,=ey`}S.󍙄OOQUJ>B齖'hCSV }VQfe⩨'чz~̇z^ F7mЮBiv ~T4fMJh1b1151bcĀmT~bֹHXScAs<.4ɱϻ}Zߋc>] ;ڧ"#qVl]yKϗnMZk4 lmfAZ{)w9ֶm~KkA](+ ؏֏=GfѾ%~o{Nw%Q[v'ܤw ! !0,⠢BX "\@Qqyj%"<(ys}T"2stof.{ԩSuNڨ|˓o$Jk`5.aieL:ɓ'}UP19AcXe2ҪTds[kΕg=k=\w=\mc !s6ޗ?+sbB,+j!c5y$SqZz퐖'ӛo9uJz|iՌk/q__ ~/xZaX'Ҿ.蚳@۰X7B tkFvr -p'mB'_L'p3 6"b)'Ҹ;@%TgjPU[&A >q}(0:2wuSVBNxŪ he[ XvzKlg8%l^ f46>֡}^F̷':lL:<θmõlІ Yn~mFs~2>` 9On"tqu1o1N>}?P?*AƋ OM12Ei\Nv?[fu8$~#Yh!UiF?u&sxGp@aŷkQܳpxL{6iiF{>GC}e3c_e\^h4{ [3j~+q^'$5}\{]kgI'9/95#{Ftc$ I7msI?gh,_mXwxb"t- Xjҽ>kۛOt q9!\6^|Xq§c>ӛWi ]QϛEMg馾rDyZI6;x$ˡJ 93l(@ZN \VN.dC9T=dP=9I{ oxץˍJ^ãC=3\NiK&|)7dX?#x69h8$ώbgc ܘ1U paxXU!\L8wcTQpvL]Y6gg$X3_itȴ|˴]隳$a5PSW“خ e@>3g#I`kt\I@_fuXqya<%]tѲmc>Ŗ&t6|r} G-VW#&}o]pP:ƸNQ;E,wl3 %^kJzw7 ;`CPvBS1pX?Fl~{b,j{QYǠɵXCuz?dn Nn#po/'=sD2jk?1٨]|sk8|O 5xO*7 xS8޹dz.<ds9}hkx~BYd&\_9vʙ)aƾ8q#⽊Q+?[O[ ?Kp<]7HOg9*J`>j={z[?}L}a,4"t͑?/PՓMu\,)粲y.+粲?Msᷱ/ZyHԥuBއL ($3"|〸/5W幓S+9[]CdB˫Ӭ4+bh'3FmTAhϦb;gOk"}ur8&s/? 02@z9K;At<>v7.kk"vnyIԿmqfSZr\_O̱ސK?D6 ׌ovn/0CVe"{fnQ^̳?8͞i ٶsDР<_yn|jmE@/쮊# ݦ4u~PW{Eyxt>[gc*`3M4Ƽ樃̣2|϶69_ ych=|+dC"G m b1}?Ӈ!(+5{?_ E}eL5;yYz:Z̽rZ^v~y lZ\;)p i~%0`"{oAb< l+Nb4x~]g@2m׹}ӪTΈ>2g D Uߏ&[RE>5q47xo4_oک{05jѶԙm)u6ͼJ5g <^oSe2O9wrlpՆ=sgN#f3N9~~צiB鋘~)n2}&בL'1odvB6ny.7_8nV~4;NSFQe3ݥbg+)"N8QL3N8Ms^{j'cshc? { ~}'|뷩%jI>٤ɧ-6os(^|>9>\#r]s1 lEwaxdέ)GM;UVʫ\޵\4OEgAV(A xѠjJ}k <PaIk \sTTz3I4<1}_adcU4Q(kh O~sYN\JcuL7g~Q'ssn !T x}է/ko#:[<ٸ3&xx־p;Xt'u(KiA똋!s61 5e\#tuB׈|] k!:_/&ظnX(nCh>5us(%&_o+ 7ڹgO!8.b- %B1_(C5:1mpyV' i^1wWg'\g: Ʒ1ʳ5k6鼎BRؒ}9 x߫_G[Ia[&Ľ1ꜙ[ќVi@a~uw5!vҼa^K㼡n*^J8m "ޫ8oc3SM/A;EZEwZiZz>tTD_=о%Փ:xˡ;2*~0:x25kJ>~hK#B=~n$TbcjUݒ]+ 2 (ɷ[ZA; l|㕴>sAx VbF;k?a-{8~+כ\'"thjcyx6LGf}vGχic,ܷ?8;@bݯe}퀳ǒf0osg|.d/e8Cn (YϟNJ>OU,M*N\L Xo.}(WٻC?)vݡ^c\k1x;~%VsJ\.Z)PmS<=Jl=:x];%eZn,"I7Ӓ~ѥ%vb}«ϻ%^]L}/WH&Nԇ q?(V }L/ co~#~\̚NA{%ϼ$ ZOJ/ E>x G=ʼ+ %&Q߀&EO$b}#Ӂ>{wvmO7f4/׍kl[5#˨ 9c5U>!=2o:ϙ8^s>`ev/£fM7GOu_DYG%_?if9%grbyi߄gb:&D z"L܎e@9uṟzަgkFKqA83%@uO ƶfd|#7Og.^ a+ZD)Cާ3̼ۖۉnrMcͱ1:s$ޟSj tK0wC7^ljynz7}xгJpɄg0?dROL'|6!sB` !|-m{Jc*΃.iS0-ΙͶ, ?2qf9өR_"<ӝMRmKCe꒿s6vwǽ@hߪo^,.4IG˄ 濢wճB]Սz! l³yGzsU9}g3<]1I>Ƈ8iX#{b"S9oMe\OI+\=v^ )W~wg c_g0@~Cܹp̅f͑i\ iܛM1ζlf 9:NtӊNkE12o*ۈf}$_B( ϵ1>'(DLxǍTUZ ݊CeRӉ~`$+ _'vt[>>f<0mJ{$ {s9{^Og"'plOýoJJft҇vЗѯe,Oi Ib1-T2#J2%s*ץ Wx«^re#_30^*0vL0<;)yTKWj,zc,2<;I= qÓ]E_bNt?f]OGS=&rF^#}d)=跮lq(i\=g/j6c swꇚL?߳U;Z]WcRY:,qcp7 f |6db(>:~38+s]L=|X a,YB !Ka_ $!q@;qȞlr_{`]:;˚ߓ>M)aۃ}{w2[? ?g8<g$͆F2d~^rS_|@\A3CsH^pdFxZ}<#|J /}ߟ-r*_!|6ʙ@odcϹ2v<ᮓðsvf{,QNw;_3;_/} }Lѿ-lmT3p4s l%N|~Ho; ~ U|+n;B~yI9d1eCZ]$gnO6Y֐-\ 2Ff#2>.HW{G{+`|ߩzK|zE|1vk2f_tʬ¨/w~yM9z6`0m4ue?8A%E9{ySqo(yal>(Vy`k:'q7|3iJ0X(̶T`-5a(b!ϫÐ9xRNLwd9HAjo\=ׅxvoMO` U'΋yqP̋yqPq~tVǒ5hl5虣w5Po}Qo N8+ɢo5P<3k#+E=!Y%+Cx~q dOVilYWȏ8j`Mʫ!IU%R4 O(ӅGKd=?DZzG{U{4ϣ9sS_ED_%G͋IWy %f'ahI{`M[o0ƭظ0;&2Ƀ<,˃̳~̃yTd~̃coWxI)Ѫ|=xۗxK(,pGqtZ@{ _ utUN,nemR )악n!d:uTO`^{%=]C~hwb7)1Sdk~útnm `I>],Wcm3JSlxabLu{BxuM:GB“~g8Gw#g9̈a%fA _ OV8>Iw)сo~Y̭$\G+s8TG _FuB'L=`cDR-cjW#k.fϽ*%5l9D_u*ǹR05[?'.7~]~-='uFt /ug;/l ;_Ncw\].]sYBd z_z U>m,FѶDM+RLg?Z2_0N{kIxEDٛk1&wWی۪k;ڮ2M#15һw֧r'~.nmيYx6c߈=` vj˻hf7kWITrWu\5fBd:TV1z2i|鬶r]kU:]ȀNs=g|Ov(ܙoaVNPn*|‹uvH#.:G8v^8˱E>_RzLue:gx`9.DUj=úmy`l oF` ú):έúiNMKnzuX7}Wu]zǓ/z|b=g1}P?ɁzIGO2v$$FOrHI ?IH~7Ү si+yg5i@yh@y7 Q6J6G9uݿ|]/ ݞ ^Gۻi~mDݵ ;1vŋwxҘOA쫙.25ُ46Xo{jj["x31E6nlcT7۸!hlqC|i ma3;e}oҳ(v5| X~ǔ~p (e`f˵NǻbydM̝Cb+udiDZv&?]dϦwψrݥ»T?lGbLl6מ&1'.SC[O}W2 9͉-XkEuc<ߕ&F— 渲9@wgi3}-g^/U7 LmI|W- )y>h 0ӏqiTyp741n)s *T3ց~EcзirQcݚ5Q6W>c9#w4 Ƃ>7mm`ȩpud֍o?qc8ػ7ǓEϳ5$6jEh?O+ƪ4O~J?ZhjEK>S 0ߛOxՠsX!+gWF.@+gmy}VIi&:O k}N) ocV~~~kcW_Uq{/! D( V@d { YB[ jԂ 6V(T(~Ap\ι3wFH/_r;˙3g꽧' k+QW=y#w6vlpE篋GT\QcS1T&z5>[LRǑ|t^ƱYca 2ZrgmM)mBk`yw||,TvJ?|!Y04cI Gu2S|}|g=}7bzr,t?+?Fe'ζxe'-kz'4,Ϲɇd#n.qzOt񏵘4^devƁ{D.5ҩ@}>IoHxhC{Gt$q<u( sTՂVXlga[l!Izn_ek$|n8mmqLq8^N0h;~Ig"<cg<&',O!͊8^'Oh~~мX cshШ{1?0KHh߰^* lc΃ gBo2Z1m# |W^OH۟%&SH?vN]?ݎ!>׿H0F0_ (Lgs/|"vۘn1Oty >ۍ>V?⿞ wUP;YE&X&J5['qw%L ?3kM}Eڨg);ptYڿ?n'Ra8w UzWRꔄ%lIᧄy9u};#χ,].ϗIw6j> ݕ4̽wԯSSBWAW,T_otd : bda]=Iu$/sYmUy툦8oy6%%Ūd=_6Yx9Y[Ks}u}'sw_%ov>fp=$4<)v>&e;E瓕|ήwX+FY ɟ;ߏ8 1/OWǤ}Gd`OOulqsRпlV f[a.Wu'䈴-5)Cx໣.yUTCtzoDY<εNl!koGx44+#`nxc_ܧ!\$1pC(h4}v|.ńWOLCwOntnT,;R˰3\cۏc[hWt ~t*js&N:ug~-I^ Gs.oA׭rw)%Gk5K5R-L=K|5 F_Z!^VN6ug mu.z])۟93tCoص;yp\ټa?s,||rF)T#-㧩ϗ.zk̗{M :3^{P#%K :U{-MOя2;J6!'MKR@>wuEiO wFfָ~#iS5Ti忴 lsh?i'Nz= Ҵ~^TO ]5UF[6J?jz9:DtY(3=k;rG"IEmHy9 ;|LOR/<]e^"vtL';$AhF:3ȣ'ZM[L!]ΟCH<@3ݘ=!zQh?X?ޕ3,`&ּd2]5G3|&ּdbp&ē˲k">Kt}B=VdYǷYGlct6zf+QQC٨Gtctctԣl:2]1;8>sPkrdC1+A-ˁ<<Q{C|5=/䭑&z=f,.z3m&NOϛ9a&-?=K:zOғ#~|'Az. e[rYwГeռYnY=L=vaf6Ky> o}/晄YFq_ `nE~ pFk?n|~|"q-ʪq7?Qx*d`g~#u(uu`)n]1B '~K8p[@$J8 {Q)\շEp >/垚sM<ņoܴ7sw)~y u1ω6[cmۀރɵ ϕi`LX8]%|?&xES3~C#1%~_ѹ?z=Ʊ.W'~~t<+bq ?n 1g8cb  sDZy?Ɖ= ~9<ɶ@XRg}:-3{a]#ew*k[Sv,7Iyy#]3oT̍H0?"Vʅ>J?'^ʕ/wʝ?u?H..vmIJPucR'O9Et0"u$>,[+u}+O[͙6;; dnfR~/e{]A$|J͕57¼'be*+} 7L3~<mҗ(EMHOx\9_F8y.U5ղƒ:WQ"0s+< UL-BG]K@kK?V㒘S=oje3 [Oq-BBETڢW"ܜφajwG~Q`Tw~sZ5AH.6)x:qňӣX(FQmC$Cj,n[] ݶQ˽ V |%[%J'XRy/%Æ_m+ûS׆se6[7|-I8R"ȯxµ:x^idǺRy\'{ ^7S0|H&N|1,r 3(Yݽ{ |KnOĜu5aj[dm* wۚ5KڡtvNS\\٭U::/ o"<:U6np;v:UQ}ĺc/;EM1O{z`ޫڧ^T႙yNfߗ>>=r;h%<^`0TXwPk9cc iC:v3^g>jOOg@mb#BCu& NASaA8" /qܯoWz^ۼV#1 UR:wy^ϔrtKkv0QkW_뵽z/ʓŎ-ƠUo_(zչUeCI[ #qPu9^E z2`kXG6A'!_+2vtAyd脇j ft7@pn: t t@L:`r: = ·^: ֹ=DvQ/7A!,ڻPA!]kCZwDѭ!͇HۚQFx@׼NC_%gBb8Ҡṱs_է WʦCh]u(${׹Qp7uF̏s!<@6]݈/m,$|o˅ _(&k>FF[Om͈&f&&oM&fCx&sMmM͏G͞N:rޯYfО Affо4Yho7/Qf?ѳ=V(*%! (^qA# W** *(j5M5A !lIX @QWTt%L";_菙꽻xxc<-z<~@lB;'Yif=w֐ƴEǜ88?ގmgZo:Qnqv bx oȄ(-2}o2ޮ[$ ܦǨ~z_aaH)7$ؾ"A|I&8%`RcG||*yw,Ht; 0hd3& z4c p]TNµy@Cy ~JL;Aз )BZ4Kw?##Df0R2f$.&1{x}=kh}[uLhWm;~;ѶmݎH({ʉvl̐w[)Ht,).'|(/W 5P_CCy|yvC (Da‘p*P1)̧,! +&cGHS$NIIw8m#x\҆$%Xw:U݊ !1x!11 pa|Gva ]Һ[_oaNɳ}1 :#`m7TiY Sڸ֤a4NՖ^fTfm&/_qm{s]?\.g,s5\\ ùF 2\sm!18=#nj"$׹avpF9ϻa' :0ItÁ7tfsXMuv<#ħZ2d_']EKfsdߔwʻdS wq~Hh`dփLn+s316'۶=ٶ\w$2$\Wkto OfDHZ4<݂~T0_@; D~Epq#el%<@c}_'&H%t>z$Ǵ\kGrouZ8RM: :m0uQW|1z )cxX:-cxbB+]MRd,'+Sx7>EW5 yu]gGKj{)+S-JR%WL_Ʒʡ΅;?foL8{ݳRe/d4y~D GK{I_r,tΦp2^EM7'[`<ǽ=_}2޵uR(lLa:Rj<ρtf9a`$q2-뇋+Eŕ"qQZ&/&^\*R>u'Z/\rD[%wh׎%Lphˤ|f7}p~D*?)ou<;v-}7ўm:3bYxZqJՅJNz*i,^Ax׋L`TN'EgӅpl^">]1sH m`}1FF@ Wb‡@Ox 1lG71lv z :זa=k[ziSw!_EC.ms璨?ܛJ8G =</8keAOx`d:@) W=W= zlAL:@/=_z _ z =Mwz;'.IAOC=o= ·J@Ox 'a;AOAO{@O h/'\'\pnH-:m $ Ai@Os˄=>= ?zc w=`_~~3?z[ z~ z?~@c@O @uL`l$/t=g z½ e$zC= ?=&Մ?=,C@O@p? +p!hW=[g~pkOwYE{N=GJ+p`+^Kx,8o1QOp4Ӆ3 4zzZ2y*[#o0{[ /)OE}3^-8LW+|1zOϴ%R*~}ZJ>.#Ϙ&np2@ <`:P /aKl}|kҿG*sJ/z>{}nCI_ѭ(!>XJZL]#.\|jUߕUE*'{?(:#/(t}eL% ?_D>'T~*|ݯ>}fk.Y}}>cWwL)|>|+>K|&}_ΦopEtUJӼ/ɏj䧕}Aw—5Tj䯲Oj̍u]]jS{DW8>եݫK'|x])|zF&gk뛅e䳱W>|)7nSݮxAwBo4]B3~WlT.>}ܟc뻎i*|qS=F֥yfn!4S+c}'3p;W }$]NJ~z|)."{B?/_}U_Պ>W}D56ͧl KEӨ{[>Ay'*O[yP<7eh>Y5O) KC7ǩ4 />>qMMܧw}ҰD}.v,mҚ_l<K%Y݇|gߔw}bQq)tZ:oDUgC OAPahl~vm+6blewV.zt'@KJIkUa>U>~?RSXu='=Y*},^X~c7vRq*=|Pi#4x?TOԋ~?'}ƫtV K?'n*yyh+OG|)W#=[ !4..=Fקe^~#)7#,yt5kqa}I0 ߷_oZ^2j=\G~h{%:zoΰ?$ F!amX!~gԳyvL=sf^xFuC>ktߺv z7ʹj>_f|I=346?.]frY(wgy nS0|ʷr&DJxIx`,G}3}ɦ^֫:u&ۥǠ3+' &f cL>8(=xO_*]9ޛpp2"9 5 gYQUzkD6~6]fw\( a;eY8m3)D6?C%|>t?9G;,onϲ7,T|4tJPŒUۄ7G[ZF7ҭ]Gl1.]曆ot9oġ{^F✲C;n՟; +ζw8w:\Ȥ?a ݩm'؃n8lz|rtBss버{5ʄdېD󷎴]֙cu9bLὰo0Vko{ SK ͱ~L~TTn^;s\^\>7g.:0\^y|.<>eyxgyH~&~:7('RO2לL!%d]^#~wE:$}~+Z]ǻ٦i_=B@,aeݱlo|>?TʳGF5Ê m{7 3>X:^~~ EL!xGm\nlsobyc<=zqe/|v&/O+d PV3ǧW2ZoJ*MTZ Q^Wx͏[usqnw}Dz7X,>s/`_Y[y"Ʈ"$񆊶|sf繥 .W](iWi ,ip"4"`3]i__,gĞ?RU\r}<Ƚ(6~=?QN?P/E<u]sQEv=ɢ>a0q]$3"{-r1b1q1b;?Mh m<8ن7;X,vDmf+#.яo|9Ǚ[$͆?dVa7YAF|n?]i3h/Fk.3p%=[ι6v}q-"q?YRw.=%ڐG+w Pa}IG?ޯ_i62u_SJ7`MSjG3겸~76v<\Y<6] [/e&]XH_,~W m~'2bhj=p2? s]J@m|ܲ|U-c뻥KʱcPq*ge\0eZi}:r2C3ӷJCؿ0quFFf/m_ +OS.ZOggZxel fvиf8/~:֪dgFFn0χ^ewRf,d9˕/g|θr֭ \ξ4F,gCcrukǖ9 +Xp\g+bڭ`K/`K}Wϥc z\ڷ}.\>+R=0;U>WFop+mp9c+}'|2` ڕ}fU1ߊx6Wٳpй4Vj2/pkz -4HC=g=6{p aN ,q!=-owy#KVYƇVċy^{53>گ #{@4o5wj-<~bNd32xia*`2}~f}+n5(ۯ2x Ujoc#o~0fly3ĭ>?^97O?Qš%1}]ES¯)|^F~R EC8hX7BfKײt5xjAϷgڱv%gDu$k@Pn+kz;u @ װ^t;ak=. ̻ZZyY˼2kwZZ_zZXlq;c=nuu]ֱu/˭g׭r=Qxš[s6gzSYڊ+gϘu=8VT2N٥@3뭜Z~c \M6p9.6p9`q 1l2؃_gxC_& Սzcb^mc vL4'8~0f]r_oC%س9UF{w5xd19'*;+f[o={*ZqyNeטp|ϫ޾Qwo2ĝ1vwD)\>P- rΉmY3:g7r{!~{#&m0g#_mZ/_(_M MO ~?U[lg&N*7Ž.-/WVw )weʽL?gMLs^ZߐiKwd=v^3~CGI3"2>I'-|. mUqm'\So\я|tq5J㞏;|,",l/l*r7C&wj37CWcfTM F\/#_𣆞s2T}1o~5~5.&ҰAw]U.gu:jJx{<ɺ76'B1vɺ=v?"ޝf[]i&niߗ(Q&.E|UKxR`_',ϙt T*io@xxs 'x9yqQ!u< 7Bvs@j"nw>Nxym}nClRo wlp[k@H 17!6,ǔm;i6{WoOJ{~d A\1p67b&aG' A8]p2c5)$ŻM;Ǎ~6B(i0. e]." w4-?'^y6g1/' :;~)ۉ},_x}^2w6βEmrcN<,eW,S76?% _S~eOnXL66^EFA1|w7 lN>kfN>}b-Ȳ~H5v vu5}N˲{%Xw,g,LY!ޟV#ޣP'S_Ɩ6j D;>8doIl[/5U7Zgxy3L:d[0]N#{#;x3C*XB#ηz;| /!0U_*Nvs+/xt<)W24n Fu~HJ]47j"r8e[ۋLƽH^veX}t4N}PcX1Yس|zl;wv>ٟfn}}_IxNL Hy&.?>EuQ`\!T;)V;SW":ygZyvtzeKS}vr;NN^ky<Mh{D.*]DHtT-wڽhRk/LZ7y'c X, S#2^rY*5:7jIUVqP0>[^*]`/7A>iuW0KhVvP%VZsў\*ȵV7t.yZ랰W\?yMyAϦ<ֿDKڀ߇Xh'.Ϝ=/ Q,=Ȕ,?) p<>stdզIi +jeևmքYc-ȵyVEC_փ<=yZ넴<{/n¿^wKA^0<^d;}CyVA0`pD. Ǒ|Nm+bj"ON¿l:~|f#ʷwkIzLiC|?;ts_8Ƒߕ,a*Pt oxi4v%*>g|}?K8!GE>o;ouql xtC2~з ;F_omC8+3+uW|Gm#u(3eM[s/5p8^ 𝽜a&Smayj/ \}G}m}}W}4zro?}tRx jN!lsf1wF|R~P&Yoq6S[97979vb@o͜ 牠7pvɜQB4g03Ph{3gN.?xmgoΚ>`eeǯ#f,wrm'טvFmײ73~|AoJgL밷$:􀵡 7& g~hӉsw8d8ϊqVn}蠽r )N2˷TqW߷S3 Ń($~k|>D1j܎JWMS/B*uh\'ZY˴]듶pw놋˯R8қ@|-o*7 6.EӔh=h/xuHΙCv|7gCUn/U=4SX WP(PZ /o2땹N9;Ui>0Cm!.||bYfgGTh7 mڨ^^]`ۺcڮYGX6mv)= Vm 7)u.|=<*-NudRp"IH@{Dz^OysFL:kԶ' pp/u6Th Y\`zwWڎ:Ud#3A;d !"x]@+ qG?MzGGYS$C=y<ɻo4kH@/siOy;g:q<-x2jj 8=2}~~h ufB5+[^\KX2A0]h_s@izt]}h~+./+uVCѭt-*+ϱ4'`f{W!W eBCcO}a$ 1-Z%ӐV\vJ!-о;,ӿdwS~Mjs)H O-(e]w]8ꪃąqa-UT YFeVG u8xMo 7o9b #x{зo!z(ypפ k>+Y5ƪ2Lڇ[^#xXG4c_0wд@i&PnZo){ఫ:kEӷex^7eR|nv:,ZF V~y]Ga Yv`rXl꣔멆?y/6e<0PׯDZ})>UyBfMJ'yrN/saa^~/e.*{DֺˏG]7o#>.sS(;'Uo#,x#w+Ҏ\w:}cCQ{L]q7<*wY[~:w2Q 6=aOѣW d³D3 «[ܛ=W#sN?OW7L6u> }Yox!:}/Kyi,`! XAqu)y8 ((E`lb J'gs= VwS)WӋL/-cz 2ˑeT͜dJ9TN疛J~7ߍכּuN̈́(O<(Gq#\mu,3GPzch{` 斛ͱ@~[l4e{2c;g)}rc/|{=snn.a|ʵ.$7gmJpBrr@鴣LJˍN{%iEtLe҅2e4Zrl[eCKQ(DY2e ,a_L\FnXf*\|tzQ>=4^M J|b!,z ӥ=*?ws2uDQ&ߍz eC!|){hO5['gn N>K/7nDh_ܡ'CFaž&C9ߍX'>^[/?A>S _'g j Za#hqTGVo{ s?O~7t~|t~"q^c;*jЬWYܦ8]|k~&w-046&t}8 VrwGڵC<{)1Zgo!YǬz|5KfU-_~G#xEǙ>Μ8ӷZ l!CΨ0~$[+x]7~`y=4npWAv%\PIs*.v|%̮$ؗ*.Jl&U*ޯl#T*yllTʫƦll|dcӳllTj>\MrXM{ޡjJG9#@O>,}'scrZ=i@rz>P_jVPSxK @/: hsv u=@mzЌJL@>@LwAXz` ]y@q00L(g(z4 a[;LБmd6-@#V:Bo ͆@z@q^31j(P}fnnk|Z*k5k_R+g޺ZuTQ{d!3Cy8axKeDZUO9iÒxe w܇Zqjk??8Z`7T<טQ\^=p8E1>)[D85 Ƈޟ#mα."4='Wϊf_-('a߁y ]{KJ;gXW_!8gLc\=S,mJ|^Tc ׌ [,mwmD]/iVe[=s멌sQyG=IG̯'J=wIGl'tġzv., qp*6v'#GCؾw7dl~=<@y,(OQf8lbx]K}H /6p>Kqw1=:Z;==viYGE} xڰHJ7XT+K4o3gi=r OxNgb< NW1khNJٓ:(0σ> myx>[){S{ Om4ʋ,nHeFIJ$\fN:q1USTG7NC~>?+}q p͞O) w/!Ib>Si~|ߑGG#s3NjU"`%_*cQ5c+|Սf^B'vrjv˓%huIK#G'L+8{ |݇Q(O}r6 >:gQS̤&=ʃ™ui[CGbv4:!7OuZ:QMP,#Vii&7}v˚|D廦wfjkfsm}ul[1nG;aލ"g&??vZNgyJ`Xl|5;v ՌAǸ>`F~tk.w߀e4Qzz#.sgk¹B&)=i<w5ʻf:2gN g:c[(2ǭ1km ]_+JV}UںmZ[s3!,: o5gJ~mC^c|nOqB=皼PG>G牼)Ll.[;f_W8ƀ0ߊ"fvP|/ ?^*76%LssCru~%α9q&8|`}E];x`2~0rܯdI,7B;Q/d &zmX1 ~~->;~b<1 }-ϲ'8f^%/aҫۏ~;5B9(pf |3~CepoG O0௄-ڙBsuE߅nr(ps?Oc18𷟰 op<~}paAk K5 +.yI7QHwދg6wc.}+*{`yt8us$Rhx+"oo1]ݶOa' ~P_ p~x~\.xS,p ǣMr~t;NW~mӚtEVKMk1aQz޺>Ʀ5i~+@qV};>k5L8 xPGkcXz(I?K@qM+8(Il55^@qMzPGU@՚tU#=Z4hvP\Qy1m=FvRl؎汝5ۙkbImgp1㡶w1{:1{0b{ +J~_DX}6ab;c c@C81pi*wqAw|GHO'P[@oMH&P#_c?;_V"T.K73ʹ i^K<8+O:tʃ;)S,HJ-pį|y_xXN&F| ~fq7?2>O3>Kc<pϠȷo ׻y^ex5ٴr8Y(]{x@bEir]j|TĶ$aIfnNz\+W ,HB,=A3-xۉg ru%= ͛=-luUƔF}M5 G5hHH ,vF ϸǃ/GNLT>ʼnlcNt&֩SFܞ,xuGM'ƳM:s~)Dzϱj(pІN8k}uɔ߶Γ"3ǺVQx[6|"D)$Izߔ Da$Q˓(T3WQ:sf!C5Yw8,;~%s_n&IvمCՅCdEɼ6An-Kv기vZw0c>QGYp djo]97!1>eSOTBSH?9lmY hJ;R}@LLJeJ; T}VVC*.9v EmY֯νۋKy0szߌv`o-JcQogawp"l$Hi,^FaQivM4w̫M:JN'^en8h3mF)q̵r4" 4B!:@+ʠܝA#kw\q0"]?)xaaYVo+Rw5u\a193'l=LFF>}Nj2$e>[l1ʶ0ʬ!C.; p ^*ֱ S(AU`ǚq]c󤐳>W`CnG )ܱ}&mj\D~>d\#-g !.ſF rqϢe`BE\"7cuu@0w*77/'m b:CCL;na6}HiWvX&FvdS1_鰚D\{D\M"Fkח(-43ElaGԙN[?pMg "Gӧ =C CGH9O ""UװL?N뵌[r=ycno)!ҏg}CLgҙf^u9W`MfRooj{&a1'!=oobܒ/1fs<3E<9S. TZQ%p>+.L`C, /x <@[70p-}p8W0+^O2ojg=[5Ox)oG\^OiĜEOrɧ]8uz DݎY4. gTboWf,g?J ޷r2Wu'P.Pt:Xw)WbؖD]aW)'f]LsK"ϟ!W׿.0 :"YoV]NQ.M>)b<úS`8>E]. +z3kgW:O"-J6w'Wv<7 h]Ex/ K+yRg~Q 4?7uǗM^M`|e+1(?,̣}p zBzwCnNU{>?yݦdڝv_9֌7m0{ {4c-cY9@Cc31# t8@1)9dlc{_(v2=B jAy o[y_~6 O8NGA/1vÌ;>8JG|񕀏3Ʒ><\yUNxL5F7H=U(;(^A/Ԋkc+zw)2 [:i̎s.+֛,m04ocfp=t _4R_TLk:JZ;4]R4NA!pv %p$%XJLƘ:l}Jw}ԛP_ZQB}Kk$Ӹ;tNKX4s]e|X~7#X{< t{<ֳӇHXA6kbi/9u:Xvqĸ(#d IʤOQ4{\o|9ࡀ𯉥;𭓙'ـKq5X_{o1 ܽgLq\gn݋% պxֵtIC]m3ݣ=uzAq2>i v40.(wBz+5,U.h|1 ]`+nƝ}wf_^fTT?N=Gnާ?O5{4պ,'z|3 ;6^V?M:ksJF*;#ϡ0_]Oy2id7 %8>>Y_ L/_x((7*1Ύ:M֑dEC5 eeHbyZQu -Ч xWIF0mYX?]L*l"]U 8Lܗyaa.X _=‹Kꩬ7.6clS;S쫧6u,ezm/veV%˺c8s|W+'0\>-8S`(`|лqD t=zQ5W|x@e"ã9c;ZۜUVO~g)iX Ko[~3 GU: t}14etV!Z6J/vu:,]D"T߁:e<"_8A,)(H"ϳ/( 7Yثy 6G' #3Vaeòaвi\`4nUz$gx)rFw]' i^0zrb~dմkh:9j7q T9bYaw8u^}#}>_#p%U7i/yLE7sx 8?Q྆<^Xftwph.;s<3ag67x/oUBk^YfY_gY?b*㳋"XD uXG-/Bqn`;K\Iڕ=ua2Z??rj;+- +ttA36_Lu8u-^ҍr; GI}4pMpG ܯƗJ_&pCڱBY6yU p !k7BGs# ;щ/Mc8lNՏ8;m>:7ŭyܲb1 z<0tGo|/hLm gUY Z5dc[ho4>a(ϒֺ8.s}Կ QRoQ?3ϧXe~\ k;WZ|b~;|V{l ,v#lW>m .^E$lu [A+#us,$9>CԥRKM̫tg<g6vnC?PKŖL6Xϡl$hxOyU)3\+m,Ο%U2z7sS;CF$#{(AC܇?D&ѯ.kbL[zV?H"ᵰOޕN%Ks{H CPl&?yxmWFhp?Ѽ$QqG&>dUY?E9壆u")hlYDӎ4O x w Vxm}{0>89ޮOZӲFlGJwDn+L% L4ijqЅFG-ԾہR^<5Z n:pzppd-e5?ZjphܽQ%|f{B4p,k!opV h){Ǔ/{T|%x3[pqWߒ*go< ! ⻃ <S8\.Sח=Vq&_/+]u=֙Xmw_[127 *TNyz3wT^Cs'Phu1>U*x`*)Hv,xqiCeD軌Z9I鼍&~א*t|YMS7VH1#ݹ 9^t9-Þa?nG#E^H7g̛: ˻}y+vz8zL'1oHЮos4z϶*wCe{Y=` KXcxȋ}ڄDYxјLE{4Eyꢹy{tnB}z &e:^M7c߷3彽iQKTp_? '{k,yfX<szKy?Xq_ >f>N}'a<>H|8/HKKbC` Xcwo< |/6qOk{D~@ƞ wc->M9X%?k te4u0tQOagHdtϐT}<,aF0_sjŋ,>ըL4;5C0]̜߬%=dr)BܫިU~ QߣR&I;##½GdngpV3ϢkJ= oeh>iqf^&g}TL3':Iw4s7O&Ayp=8,!2˨LKa6$}}^Y}o%Y2;e6e>2̭rX9,9,ytLWAKYr1ƏO1W=ssx>s?S{':(Mq{"쾹nՇi[ݔsμXL;-LLkd9qG3krY r@q\&(L GIYT9=ǿ1ɮ.'r|cΊY˓g?ki{*H].4Yws" Gu<<.t\X$z u߬'} qJ߶ڤ2a{JvKnc M*IROLvXF_gpU> 7`mZz2r ̔'ct z_3[ΙfSvs #=יxSunvC6NkV]DvqV}WY]-E>H ߢCdۣU>{XXy;r 7sZVX7ronv2Foz-\XKT;6rzp~g->c͖ ~i@-t޴+;o3}pjܷxvH t[m+|- eK7ϰ xɑl [ghp)mW+{w<92pL9<Lbm"c3.K8cA+,t$QqB8qazr2e§rgʽ2s.=+]}(i.qv8a* -+uZ(7: m~-o2v\n]Gw0{Ͱrsr3f.-7c{ͺ?㼮)7yfx"ͽl<rPv>Yq׶Rz*LVaӯ70\Iy5ĸ"wF&kOF{? SE5Ϻͽ WĤgV*4uڢێA]!H8y -a;sGŸ7G7Q.{eT#wjkrA/t)/Ͳ6xW~:\")'%6 Q<"񧡜/T?Y7D}JzQ)?&w.e~'w<4oV +Pw{7R/|Zgز3'@Wj>|]w˓~gWN$Xy?۪f3Hz^(<$Ȯ  ? f\8ԧS>*S]8c~nޠDf6D{:x%/xŏ[lZg,c|Zu4*ݯW}UryՊO~aʒֳεa"XuafR8LBYߊ0{cQUf]*3]*Jq*g/9z"ׯJ*IaDrPQ|vj<('u!J.+Q2;mx۔C,~g; w:Z 0e$c.p= /Tre0z55?̀b&h?nK<{U9CmQž|oܾ[4AS?BPw%#ua Ϝ "]'2ٮx|\dMv >!#:Is^ <ئ_忱D[d6wKU|nQ|:F8|%x>&.v31AyA9A΋_¦b<$^ g˺=mٕG9ua~FPEMb0w~{l!s)V)ՏgL ~̮lr:=+|~r;cu ^c.M_e|3}7y7v*=}5F=9=Gw"5̀lu2 Cm1c|>1{C›‰8FmyWEޜ_##ρ&O0{|w a_!8^4a䧸8 g{RF5H?pY'ıG0k s0/ Bz~Y/;D\3o4x?~L1n"}S0ML?lvFcRŧyϳNg^.Weg?1zF7Ut*vuz5eo?ݵ:_i=$ؚ羨XDcODZO|+x^σ$oHxvN"Ǡ~I^g{xj$p|i;WN yD A@4k~d|t!,qﯙ<ߙD~YPVLIb*gn׆*b˺e/mŀ/4/OrXLsjܛ7.p]g_aoIbX$0_I$^mmrz|% 0WRIL b{+{-*ʐiuVٷ1.p/Si \>7X7nDuE/$93i}!OJ1/鿅qdZµ?^7/˒e;` ;>CxoŦ=>t?3'd,/>,`wkŤa8>!,YSŴ5gLYs,Oڃ:Fۇ'BLVry5|V ?2l i[bw‹~]U: pW(< ž@w&A]ϴgy7˹V$D*Xvۜu֐a9s7g(qg[)ʡA 2X54I 4 A ?#8ho-uSxV5 o|$ُܳ34-)?Ia?)?S(H0sp}Z< o?ENs_98 }qE.k,;H'nTYb\<(`њb<'iǞg,=R&)'v\cL/*n^ g#<7\/<槊m~*mEC\=b~Te/f/~VI e|`H8?8^ ws*sym׹C1i;<轋-\S}A&^JO];$܋Ev'GϪ^balzˢN4OdZheLJ:wlfh˪`4]Xq]{Jƻ7`tѱGK{t?q\eߌQ 9wmuz=6ͳEL,vY;Yh uM|bw7V1 uB?VJgަӴWkg>NrP M'Ӊ1N2:]jix˼tZ3ügFX1~ܣdt(=Sf\[ =s@'Dt2;M5$ K.%PWSxދ`^E]5_|d={:V.4D< b8M=sYٜ7&0ʔ;2}q|_͔!z( =]C{3ן0qN,JY$g ž~Eh~vڞE,:{Mgqdvp6Ź9Lʦ8gyl:SM~IjiNj6߆lYݘ-/Ň˦Q!a9B%GDϗA9eD8)g0*ۂ>+9T+sgZ9T[rlgvrlP3Á}R\|bt6O ]15ﴸ=d]{"3wHsxQ.祎O3~}%W]nϙ'{/'"?ZokTv<{W͓˘yb8O4.AxĖi0͹?mnjRXJ ?WpG|lPm"(\΀7ͣ5iY+1<9{18m]Nx=|f߾r1v31ʳ11ʶ?$px|| *`wΓe4#̐}ci4즟XVol瑜RKrbd.ɉbﻘRb4/srIl%ٲ7dˉ\-]H#22dy$H}.y$_#%y$OOOr{`>[>$#s>|ee>Kә˯|V@g.^;sÚ\@ h\·'Uy <χ nA߇D8 s;Rٟ_u;B1xU(u|dDbBzOc!%~;ͅ|H!qXq"ET#o+:!ߘ KoErov2'Hl:n|ȕlw$,.skS~c7 Wq_XqJ 3ldCߡ84inh>11T>>bj^(PLbjk %~GǔHm41L4%ƧJ(*4~TBil*4*4-U>nGpIsRoP2G)RJuIRJ[9ZZ*_e2=~ D;uѦ2rwῚl8'8No,#ډe|h6L6Tq8rsmgo2*3ʩ~.(̹M)'Sʉr=x7ˉr)_LbW9+g~ . wm ~S+߬ ܴ݌ URA {i3F4;}+}*lzTR$y;u$oUÕ4VҘ\Icjn%%4VҘՕUҘA%VҘ}4U|GZݪhL=ԋ*9}K̗&TyuiU2_zJKU2_JҜ*/T]IKUT뫨?9GsY'\REr;ߖHznw$x v^kb"]@zṀw^ a0H7>}c Ew{MW Ws<`.B9ZΡoϒuQ/@*潡bk-0|rzCD;ީ:x?a,4$kgQbO~練L'::m?FA]mѾFbzhc͙;^'|1F>cgXt/ѕ>b1wQƎcavAQh72Ɗav)QGzw]XfM!1Kxǔ'6~Pjs==f^S{ 3uζ kεgX[mҶs<}emf@yg8KڪD5] $'bHN7X@r'n9yx]HGC؏Pc9>xAugdԮ. i(-ia|o ?]-mAtB}?] xTŲ>3Yr%(Y?WTV3E.;0"I&(! Y'!{F ^$(Td{oLUu:TWƧSѺdp%}^SSmb2ݶXJKͽD߷#Vn}kX]gw,%gAn Xv:c_׌:uorqik7uk_DϷ4CW{_y #NS{d/oN'Z80\qw!|o'-멟^T<%><y"7\*zfsq$mg1O xiبD|O$R= ?l3~>[x_->0u0~'h3A^Ƹﱈ1GgE }))A&ce]ׁ0,@"LJWqZϘ^nXW62+{!\~>9d\sSv۾/2_o8ίM sj~E5wTKKe`,>mFxGvk|q̇:c&2NIqYbF9b)xht8Mԗ1qcW~Cs^6s #B{|c![l?-}yWbMꏤ;trʇʗ=s(a~Q:3q=^cY*Dk\qn>m틇+˧pXqyܯe6mqCORy 3S3 <9N~G1mO;/[-p?C_~P<ݡtM)V;^p ` 8ubE=!@ v'Ѿ(H$;{<pG&\k]y=>{'QŊLI>LBp=I"$igxofuz1Bgvfx] $GoC98^Tr@ $0rHNp9~Xm 밲ؿrO$ FרzU2Hi/P<yKǴ3b;bQ?Emr,clgOdm2~ ppzK}(l;?Nl~<˄/ c<ynL0gm1tglU٧|{K}ɕEnQC%֖yD,x#`#ف\o?~w޸] Пcd|?1>g){w6Ҿ|Ǡ]RbK*not_Ct0G'cl cad'|D>ZWAݮ]MMaA8Fzq !8XQuf2Z2v;@/DM-i1 *7bYmlWXBF;m\ˆ:u::<_"LބrU̸AʵYV!@ǘ)?M~vj;@ȍlB3@{C$O ?ء*!j] ͗u ˋ9-y湖p?!?|QG3~GtF9ml[h|\)-BxHg|Ucto2oe|s_ [t~e~.2Vouw%E9?Wn%'uNdL&d< 0M6!cxHZ7֫ϲfY3,{EM\;yPI62/@^A^:BswA]wgnIJm]BRz'22Czl B Rv<MAA޳1sǫB:OBƫdnCx[렻#Bx O䐞i Y%?r79}6 YA"nuRn'.xv8r$@_a,w6_Sq> yl9,#Sj³rY_eY9BVnHV" Y=WqW9)KYrCwj^HG S۔xU"tgϟ*!kŌD@:8%dywik~A߻KۻɌ8sF/ Y% ^T'{\;7ecyY !dN,[!3('=ئ'xy_,Ɂ~ީ`eWsɁG< ecї8M:sAqאI^ؑX^׌qC~狄,cY^'ہS~G P$02} b,_> :K=!=4\4:w6W !'GI g9k9B.9 \(_ArO O!k&tMۨBv?BɿKHױ0V+ǰ^ŰkjM'?}D~zuV m `Zҽd oD=AA}$;=BAM d=; }oܟҶAl5x1nͅݾWЎk9MQXuyrO:Rmb Hk?'tA;xenO^jSXOc_)7׏U$,A怽:H5["F6lA4hrMAil4i "l)4LsEfCAS4m4inє 2j)4L3FS!h*&F_g6JAS4/hMӼiDXdk3֔f-$[]&ٚ,5m"[swٚdz"[2l,yYdd-?E,ͳ]6y!ޙMvl:%|QfӞUٴuC6yȦ=Uٴ>l:lz:0|!;XőCq$Voƛ,+sHgμ};'x%S<'sI-gHCgx@eH k-_ů6d8xguact1,BWVpʿٗcwVZX?O|Ӡߐ-<ij`=oR1uay1ʜπY1wO,`!.s*,, ȧ}.߫~'5@48=5,HvT$Pv*!EbƝA΋^x ]_f<*~_s}T;zƯۑoQ. ^x-6gG\|~=V2UX7+t;ZKrk7u,fNUoxR?:K }j|zOۍUo2qT&^+hc|Mgԩ @14Lx}.p3kM e0]nh9"Ϙ5ܡ@9Prk1F -(ntG4q04os㹤f y:tWo?Z@e:tQe=a*\kNd#10*֟X&InDeBbVߦ (Aw[.}<R緊 z+/ {uQ7^2v(ẢTyrSmJ'|b6cʋ:( ' s~Lx3,OxS&T!Ok.ptڧ}TG|>YMK -{꽦jlwG!=XHvqf!,BTRX)Sγ,oj_D"TD H".y"}O'}dg٭Ivb=x,&3i|G4>/ї4> )%4>YB㣧Jh|GG7h|)fRm/XU)ΖR2 )eWF7h|GeeeCtҦݣ\ǧRsˉwL9>UN QNeU^NeuK+H*HϯU+Hϻw^%/*sVUxw(~ 2+xuN2F}B3iXTrvT6<)<`\<}70ֱ=i7cf%_X=z֟rWqxZ {Mr !Aw;Хn'&7#,Jal+.*#+mWR[PIunK%չJsTRPEunD:v*j窨m_Ys!ռ-trLPs { AakQ$} kudr/-ų&]œ,cQ5KVjj;=&UnkIGI'I?Vn[Ԑn#jH=kHjHjHwԐn'ԐݜXJ5d7Ր|5d7}5d7w֐,!Y[CvX ͳ5d7M>Ԓ.%]t%]PKt񿵤kIjI1fs/mx?2b[6- \]+d~o3*q5ZQ'O>t#hW3}͙Akk5/cwyXTuFMpabMxQ~>ͼ,ƻ>CenZG^"Գ:K\;xu8DT?[[a8|.x>6 q.Y酊[2d!@KH~ %lTAk5ְ+rMu46(,޽TYa\YX'0):߂b:E8_So2ڄPw2Ʊ{u`SX]Wyg<;:U2ys9adzּXvjﳜ@PM_k4l{L{mˏKEM-K/ReF._,ܱ< q.U׊rr;0 .y=(S8W>ƭ:ڭ0:# 0y P~Vz'Лe@3ך{nfcc&/k3gz?1l`Cv;-[٢,c,$wnw"Xf Uw}Q̶va/B~xiLnw]c}o?}r+}S|[܈MncVl}$c5oT?*0 _D!LQL_DۘÙJuf^ͽ-;`J*u;,y`X\FDVE,H|(""$AD (*\ 2OEȸ$ =!s9NIϛ{=TթSuNq_(*0磇!d=#3}'9u'I?cjeHF.4ge/q* #M#DPf ռ, 已gk+;1 ;s<`!ncX,썄کe|M8#"Hφ!\XdLOg@ZXПIW]Nv]]FB8F U'AH} lzlډ[^,v:x''/"Ϙ{D2߅dz-w4z3p ĩs:ɻOțL8\s/ZNrrf7۰p|gѳo G?%7pəG䄣OGI#Udm~m$qĩv=x,紸?Ǵ79"oJD@[mgі'|ha^c~l, =a.QVܷe}lC8i"+E7*vAi~yi{:ۜ(1 }  &_8ҵ̍Fq͙19>;I>;>;􏄡+O¹v|X0 6#le 5bDq뜖2m g(6BDa I:_cYq +gS nd+c , ?9m&0q`Njۜ&8?cC2c]oXӛ/˞ksu ;TqN{H ]o8p 9[O9;_x؉sͬ'cܖrYsڡsl_޶w5~@?" epuG:8^3h8ecXɰ>d8fx/xk~uEiθc #w{>g\iyMA["#}(g3? Orrio}[eA|oc"O ?`R"+Gma!F63ci4=&IgB޾sZx"yOKs_O B"gys쥥&IA!2BsD%gQOu$e'п G/"u>gz(7bV|aL9+>3&Yc {xΰsf|xwAc~sT8|r#ri-y>sVt>ByB'bVPZC ݃y]a3=v3#!F;w;iQ.p1x"P(\%&5Xk lf *pxV~7 OnIcxҼslSǚzsIw?<x>-tŔSL} hvZpuxj!/X^Kr/ 9|ZX7PKv "zGÙ>iZy(5*/=O/R# WDu$ߚO)3{/ߟJ*:7Ӽ$y6( !\7#9Ks+¡ N+8⹗5cW,6$ :$'|?;:#^-n  זdʷc櫡_Qv<5\c?\YSܥ$ q4,D na01^.gp-pE -p+~Z~R'E/x T% N^+9x`P}Yy'r|D<{ CM x!]' Kw\l \' |FFGOvHgp޹J+0{nz՘8\gu?߷,֎wxgs sIp֛_wԠnK{gmsJh>=), Z@~ k!v:wIZ8rOnyO7D "/[/,u qBB|vq!>F~J+}j"ܧ_dbn璘X6cf~5\dɱtV:.BnGjJ@hӉvg* B>~VdXWDYlK'MQ*OZ|hh9w.;au1/ *Ms)X)..3pRB,Ks>;ޏE9|N>#!hFPf2>[ S )GW 7ȁŜ^'Sy= tD݅f A~ӌi,kNc{|ڜ#4M:Ѽ#hxmWDshS q`;{b·ˈ=8:׈OWHY<Ĵqmc.05\̟Ncz:&lCC 'oMIdiW\9E~s~s{wB|}AQEڝ^zvY\@s~ ȟ/zCYԜľ6O]x,φzjJ;9M?L5ska}ۮRS֟[N" s5.K h+RLcY,SLYJ>-pA[g O "_+>={]vWwij's˰=y Wʰ= A9?) >.ĝR?{v %XJݞP`s}YyiT8`$VQ ˤ  Sxx"z^Sv/6gU` $aea;o GMZ]aMzmǭ\W߉FO>:O 8VWNFjcg@z!Iwmn-p[/' D{iWq\JL ( rWy8 S+-D_ie%QWu[%Q?DK%Rɩĸ2WbFQy12a/ض9[BcP`|6&n*x{~U>Qʻ v.B?eMrXS_U>{gN.~. m+93'>rkӷw.j*4O*-wϐ\!ajSWd^meh5J5ʢjh+{VVJM^G<ήA}jǕ5b b slo+s__|ޯ1oyӔr,y߻C?`I {- ט `$-a=.6( wn hMc\<%i2'5&~so>5vؖ!lːުֹ-sB qc5 >#Ǩz<۟1}VHOӻ3XvWʾtnnA_:I^?z}[yT^%jx*-~NzX4 C^_4;ZwkGjy-kź֔ɑ9WEYurסywnqFkua$>&b|01ɩAi-[__DCZ1®OUe7[JIC;C33:uÃ=BOǂKz#)vj=0zA=/&z훽E؊UYol cbOpo٧uThz&o5ڨp`XO_cr\E|.;tG?X ܨGS-0g;AP'DJCi9;۩RRyt-#^O}T~Q^]!*2]+c^' y/2_Xp{8o yf= #tGOZnpr =__bƲIwd|d! h5``[g[[eoJ#/x6!WѺGLgRC\y;^I@G;HWDs6bЈ}IFK5vFi;\Ṇ.O z6[׃F3u ><.nmn4><`M<<1tqjMd[L\M8>wh gjŲ&,W,v7aYn¼?քeQdtZ2xotڳ&:B=CWit=C Q8x'wc;E bYj34WpRq;C[g}Cx8>c Ah͋Sh{'8uQo㙡z1( 6w3{q,%Ybgyܖ_A4/,S}E?"ɅmCer ݎk<Yf8 M~,@6o{ LbǞwzbߞiXlod|8q|93hs{RO.-Y_WϺ(0?6Z|6I_v3> }5$"D&8L>Gԇ6O Eo57ɱ 0EpziS{1wlпЖqV[YZf`l3?Yd 4e=DY:Q.':DY?:QVj$Y]( e%,.u eMr8].'DhZ}OmiHύc==n+-nmX C['qċ1gǿ:?k-n$~w>H HSOqܐQ }d7y/ܤi>'ޅ7u/Ơ[ R3KFfFPŻ'~ hun^A3PTi}QrrrrՃymsmsmsm Gͯ=h_P\**Ί}X~kIx-5 u[mBݾBJPѨ[hmT4v_4T4=u;EnQ1[BL=v|-Ʈ xH#̯*Tkenړ0X ~~XP.=(]o3q>tC܀k=#OofJ"N< R;M|𹮭Dk륮g*[kQamƥ;Muv7|zk+a<q7yV.j\+(0nImb b-g]hu\9A̧"w 2}N>d%+ZϚIa'|m _13` >(ߘ 征]%X]'$P)392EuU՞Ja(p跗˝5H;|B[ DN#Y6kWc,9mݬA{w+g෬\olcӏS! `yVes#zFn'U se—vw-& |X3!֧$bcK&M~q̎ s]'=M]Gw>|1NbJ8Ux_;W"a_5~Zû'螎uQ,Mzby>I\1*P[OUg/qSb/*5T\oqY.oB=K*mH>*+:qhW9WzyC\lLW{L#I\#UtE#C\=V\gU 5M%C\eHuH\1ƟUN}=Ev&)o+`9x!Oe?.I_XǑp^G{wju>N*iύi$|Gb{DmQG Kh}#)]RVxn1cZlGfl)H"i7 x'8$צ!XLmB~975!t౜vj]m K'xӶ% 6~jK=8^ʟUxOiƎ1J :1ctCd^dLqW\_?Os<77_d>>#l as:%cE"] K%XA9vڤYںraK^|7e}iOyMAUgfh2 a/s b{186?Qskӏ״w|wi*;Nuow\8_Sչ&z̓Dp1̟1;!< c|x lj慀9෈ў'Z@Zj|t hfO4B[@1A0'a/ œ?-s Oa9 aƜ9ˏzG`|c0t CyO0t>L{!9<: cCBT߫!3 !_BvP.C!^.?^si(xn OBPxR,8ۃoWwQ8ؤ96y*6쌃M>\Il|}`#gag[u/~ {jxߕO'KццIi?d-Yw> 7}PLy@ ÌF{"hBDԿ>7'꾧DL; $4 f&&t;)=w%$> )=,'$k=X5ny8'N 񺘬*E7e @]6P&ڍD.!v Lmv tKAO>S[Rk'S0^tKx,TsS1^T[[ 4hK $+2b!#72tȘ;,M}Nt(&z AU7YA V+b A3 ڞA3 m+F&deq&dτ982e\bM{{g(;r ޴vɼLc>=COiݧ"?_B=eY5Y,,ݟUn ڣYty Sl_ gÎV^6V5sw F[>}4WFAKA_7'so`>>suzǑ A]A]. A]v  NV.hjrQY] Vp/P.c(l7jn+}z'}.3.7+1}((\ ~7Qxa Bji\CUno/ߡi^%g?a8\g^^yogl=}|] 'B  'J 6 ~į Sį "~*Dxp!ƤBg"_|_!O"_b!o"_|s{!_^|qz5EO/BET|"䋿/B"})6sŴ({?nu/ʣV9Fc Y"p1sIŦu6^姚 s}^lZy&ر*FHG9υb#< ;% Mz 9bh7zHťQ94bGwBZZΕ=h0xe'9_g {;yNtPvHcuxe|S_81_Ht3E34p k0~hiW X.ރ*qۡFҍ^kðowla:l ]O>[~ag0YLc81O3d_V131[g[_ DF kY?m6[G@΃#0Ϭy=euyB;}7׳K /Jt|sc h V춏pOo׿KQҎvo,p ZXR=scOX#+v7v9ׇ #Qk>o%G^fZż ~"˰w7ЪlC>賯>0C3>6VG' uO(7J$|($%+g8v3 meE6l-N(SFy2ʻA2etF(c+G,[XnT9b#{ܳ嶗#ܱrr˙b+T @,wKbZ*m@,sq^ruj2a?!~stDZïҴIQy/Tj_sq8$Car~i(9~'W[Is"hwSiݓz'-?\eC 𣕦Ln7Ҵ[Gm/>mo%Ngl? *y.{~+eV]Z>WH6Ƴy{Va‰Pe+ ˺ Z ˺7s>?K Uܡ\鏴ua27?`YK˵f. ʴHo>PUT~`VGF]'S Xʛ"T}Ig@׃n vg0÷|Ǐqb)q/Ǘ f8Cd3#4##a!`$S}T!zhm(#ػj(uN4QZ .mE|y&iOY?KQ{:FFNGaFk{H>>;;8QB)G& Xne1^ gp$i.:ξhU;867ٰ0b l361s|Qc(W:<#jXt=L};o CMc概c4Va bH|]YL6yU@)j ^Nm̶ fp18`aǽՐyfCk`{H5X5 ܯ U>?cA[? %c;~_& xׂ&*jў4ޘ\n'~N@hiH0֮;K3'vDЮڷ:||5 z[*mf2j {ϖ踃xjހ$r&'L;&љI൉hL/C>>Fm}<,}H#άAD+LVՈ5OlI{S lU.,ԥưz2x,9~ My9|kw xC>7=' YSs >. |2P3C'#ɨÄwd(b L`X}EPr;=<S@w}d Ɠ7D⭑x%?%qOȩ)SQ_SE*TiL>Fiވ֎V8+Kg ۦV{Ӭo\=) HS t*ic p>Bg)e;[3=b5V+\p% *tFvvQ/S'9^ x3Mh;.>G_{e|s¦ӭ}B>tkpt_ytytW{'Ksiea&w[JW%ȯisX&k7:Qmja4ۈ{5Z{3>T up>NN+^ IL _*8";Kxt w1.ϴuיf:/S$yr&'kx(jLg_CW.|`C %xq1߹`fZYǙbpK_IfNC|%|5if !J cѓm|gujИ|[U*G ?y82U`ÇFV$|6Qu$D6&&ڶa='y'ZX5qZ*W($/ЮȟpF-l7'nma{ o{n:*:Fk-~w)~OsJ۴gkA=\aQ!iN|| Ĺ(e.@üPexSeHkAob^3_,u}$\3ұ.Uo寫wkq;k2l4%'hӹ7:p}әfEgGANwOtt-{tSi UњcW;&{|gqV:tow֕ wU+= '|󐐀3gW"e`B_$ypt=c <9@ .ڡ +sZB<DxըFފ6_'r\6|`W򶪛O <~IK`m[G ߩ ND&t߽lꚃDj+٢.sD1~WN;mǵlĬFdmL&wC}wP]sRCxW]&y8&yE.˱ğ<&ܜ"/W@Xi<7oXG)(r,:<y]/>";f|,A]q ')H Jc&{ 7 A'|k>J;Mx-DSسsfvvs& RlM?Vǹv.ovP`c0s?4w79hM:{Mj79+VeS Vu z7oɵ!û{tDwKR9y%srVN\Tkǃ7ss u~eٺ<$aCؾy;8Ox59բcι-qc_|{w|ʗ17eX/(þ|wz B\ ~/z=+H{tҎ.}e[ v som˲{:s3B~smf+&J+x?9LA 2hö|?̘m]oZYYVZwlΧ} #agX!Pt(?`OMq?f>>K~vM-as( /Txp~K+Cm%1B}aG цĝ_'nU!ҪBQ!hk k!a" ("Ћ+><'7}E㛊`wV'E8T>vbTc,_ {oXD1Ɔb y,-F@x?6w]N'r~9Y J\?KPJPq%V:|v ]g}1znF7 wUu"g#? [.mO>SWbZyAr5}ĞBcHc^_`c=Yl#wA3.h^Mx ?ɞT!A«]04v,kI_:{+l3+Pɧlg+ouw}˄yb!\.~ńk\U`Hy YjYܯ}J.Uђ%Np!J&::?:$} -,<;\p% ?!%^=f=E~O-o}Xc/1G0wBpwl)yعǾulj_-D. u1oGs>/oSCdSt`v9<#SOi!N?V.e~wQF/'~cREY|.3ōRf/(sx웄Zu%|gS( 'GBW-(oh!& cLOc)|k oǦ^v鏗Tw"SKϽWRx̮W8K4=,i+ҕR uOɵRgEF̓3>es"#n9f[>ϯJ/T+| o;*Ne ?_Wx¯(U&,锐Z`m_E?m ">b/r]v=C9-viܩY!uVȝb^z{SdK ۍX"-ºmUw/Taݷ ]UX/B9B.uV[~Ό˪W5JӿZ٬ b0ڪrzҨWwjL&t csS`;dr[UjzEGpHpVn9cqX8/GykܓGMbU_ZV;޹\h!/J;m\O{r*[.0WXJ{y}klۻƶk0QWY-뷮y S9z&OgUN wT<C}ƞu|g؀&{`GIaDly'L+ ~*}軳Qy&9Yg{0O<=U*owiׅ5ʹ+Y{oU~u53JȾR-5zUoH 3[h"jDCm?ŋ 2BpYCej.>Pr-zϬޛpj$s Pe@ש>|^^,~VkmՂ:Cx٫R'wF{u s|;u<)4~As`_2#΄zę!qKz䣴G>G>^z=d>M=dvAdK&Ȃ5@m,Y0`QdȂ o,Ȃ_7{ og+++>vq&J'EK۪vY/qߔ$qmD.{cܭh9?fmUc+済|MqΆP/q~4k2%YTgQOȢ:{IͩYYԗ˩G/fly&]mra׸Lw?7d55=@%|F^p1_iW(W94>&^K_q7gƅӵy7w5$oL @#g~{|@M\5&1N_HEn=hEmE͋LQ4G$$2SWbSut%NPEgu:KId=pmvtG 'hfiB]1 & i}mCgw9/G,X'ܴOgE-|`!h8(eudX(~r}o#7@9CߑxYsh|Mɡ5wИq{ q*^ X_… *Z^%s̾IE#+-Z? n4Lx 56A]֟mHz~.M*/Z6}UrI|8D{T>ώϝ>oUt~czy=.o!D@cs8xt V\{'>i>eہp/~;KoR1o5 :5e3eE̖!ma{OΡߦ7\ӣ},o1?Q2wu_6%mGJ[{reWd~kYn;Ã1)LA  Wd?`gHiuϘςaw^lKmWbyn_Η7WcA ?mSre-/= anԮ2[}\7\\:c4m s~T+9ϣYVo6m;Nw}&Wi:]?Nt J@7K%u7''^ bR hMQ?I%R=Vi/A>M^w$\gj<=|#(3H+'Xu- /l~wx W|,!~mcC7n|r*:`j߫䀸m>_?>OČa`LwSA6+.buGC~#+M8Ľ"_gA'Ϥ`$:@.3u)TeJΗ(^n!2y>϶Վʧvh{1ym9\ 4Xd3g;L𷴝;8;aN*]U9˽+<Dz\vְ,^= &暏T&}W조 ~}B}P[Peow}lq#0>2(51kopCwQ+ݺt/ߙHJf .$z /l \s'c ɑbd}- qr;Çq8Մaz2_M:wSmxZ_<߇\2kf=i#!t-(߷NmSB:xLO:8u<;GcgsvqY8.`1Ʊ<1co c62Fk+cXt_ ypy0rc7gcT _Lgd|qSt퇠 '[dyst0b4k1LNUoF-{E;bmsϴY>\_~l5nkrW\ PAywn%w0-НApL2X^|OXt}3Rv}v'z9c(kP Z%s ׽M>>v+pXҶ_̠A9sfPl)Ay1vmPM+ad#Ob{8lA⳹ c񬩫v%OL7`YFVpʹÜS"}M]j9oLs1G+Ac(h S15PL>8خS\KEtW+M WpIw쏲1.K_j]wuה, v: _{J7zŃϯ1qOZY4!N8Ɔ۾Ʃ OO85U s+Nls8[tm{Uco+<+wL#Mflm,P|J5ϨXo)UӘ}^5TMc4fQMc#ԏWӘ=tv2XMPm5og57GՐnغ_UCYCyQ5Toy5ta @4e{5ҧOq m}:c^[CyC9#]Ԏ0ƾ3WZ{c'YLmci'cMDZ\/aHo9e8܏aw#k(~N# ֍q_Ĝ;40Wo$١^2}DO 4$;z>#Oz3#Mmp`w{n/JQ`;\T[_w$l>O!ڍdIJ>=ݬ-]u׺SK:!uaaLfM4"?qu0~&}& uviw1ur/"IFWBSg3^ :0^x= ϋ6RëVC7ɒP/PI~r~ p=~2^|i_9]/2ZY/2ʬwn(oPeIvQ9V+U,/S4mn/Tڄ]6QyOl Z7ȸա4@r@e@֬ug$sl_qm惶KcQV8.oHrXP""!dD='x4z>0qcD]cX?G3$/ӯB]#҇Z-,{O`_p2,vX|y;^XG7~m6K[tUӷQoWZҷ-/#0sanގin1ye Q+w S34>x Ͷ4/ow>=F/w1̹^3Ҙ17t椰<t,o2n7gW^UϽ0 lsQQ@D`Xb AEq(ADBvdT>U^y;s]]n(a7pޏ!~{d4V}\tp}wlBgظuLӓk&͑ɤw$XLs׵"KN_r4[lS}X1mm}d{8spTOƿ:Ss'䜤1SzPzBl'fpYa[1$n&H [ ^q|<d- ߨ8+|}Va<}AbRLp}F\ Zw3,}@s$c?])dS/v:{,}]^?rP&sg=swG5%GO 1!ЏiwRd2_Rnm)|'j oT?%0>Ďu7\9 ;9E$\v+9Ub=׼OǹrC}!R~x)0^f]~Fz5k#-TG{f^gi|{SSnf~fp{<*wj.NO3FA0Z{Hu[*>B*j6hgr+8~?Ҩ??Fzcv}VbZLc:NF҉t&t[O'EM:-3Hv s{&e2> oaC`>2<2uYw9hvp:^5x xUz2 q[&v0y%3 3ەYqrG ~LA$(*{$i??jM}e{RkP/k~i-32&v 2밷egI$Q?ylIko^Y4F㢬<>k`*S.">m4x$5Y"lx"weD8󍯩z] pʺ;kA*V.DUY8̗| [B/mw7` t&u>{I,s["7ħ:|oٚE(3G牻T_v0-:p>>зeњiL~R?e,KGcZ7gg|Iu`l)l)lN$ R}dhޤ6 w:IMl' WW!F9}=n dkۆ~Hڗ%5k?cSf9xR5G'*s$w `nS gPc6xz=c=͖~hD6wJo7cIN&]57lңg.Ne[_e.?M<;EE63٤ˣsHȡyUWަ·;$um4;ssmCgB=~S>k撻ɑ#9~=zF8q=A4u9G|7^뵎w_h0^6wEr:W0ﶎ3r630YV{,KFSg6E|nt"k}.:K߶/Afx˾k,C+;tyiTw1rn>oc E_|!USSZ'ǕYw?KՓs%6Ai)2هJױceؚ:$ے+yc1; 19J'we[[inBQGR):XKr \Xŷ=O』_~=$ I.]'p$\'Orik~KvįKAY,b(b~?ש|{^.vSȋnRYx#)7wɛ91g͚9gl1c{XpsM8xߘ͞佻F:lglҷ W:SN]gUމy+stwސ/ԷYw8| Y:|Zg[v|һʸp.G[Đ|-dV1\{m`+?SƮйMKWq$?Is`g>O|ÈOێS|+9p8À}8o9m#?Wp.7L|ZG^ߚ~_(_/mt7?|9o_&O4nmbc_aY+vóOtg}"{HlYj׹*@b (=p=k0GIQLDx!_܉I=E9{/)t YA ܒ^^7ܿw+ Eoh[z39x Ŏ<_zG.'Y XqwQ~qjA'" *R݅^/ eB٫z{a޹K e]YH{\H{\ouӍcYEt~nvMӛEEbUSz݋Xji'4`q=>>ЕmE(0MC'if4Q&2F"H"ۋ6og2~ )_3pXQ8Bo׈u{p~`-\ŧYeCuuG`x^zGó+qT Ɂ1!>S2*;dK/}4E*+Hl,kyA;^ S~9bx)ކ*<@Rx0AŲJZ7h>$KX0{q/b|1نKo'.bKz\,smaNR,>?}?Ad_)~ʾrV!ݏ4OsĶi:mD7>tu9n1˩]6Sl/v.YʩUP_ 򋌨 |Q{TAkVPl/WZXY͙$_WR.z`Iz'ϷWdO.̓wWa?7z==zB0c}451{VR| +;pi'ux8G9*e޷F_Hiٳ8h~蘿$q=5 /p{K^b+]AGJ- s[vT\)6X]`M5N\)ƥ?WX>0Whx[!{]\EclPtD>^dJ49lN*/W8kh~ZE*?T8Lq¢t$̫ܓc?['1S}_Slx_CXi0~@Z~0g ioo+u>^W6KoyƟ*- ̙Dܪm+^:מc=+* K T},_zYzdLq Oq%JN!\`.xv7$/غŌzKx y5gYw3͒طxU;! ^B{dn!#' as<טODR}G=o绡T~z[>bڽe~7dPH.0M?s0~|0C޸c^^teNY\Rqm={-\4YGU?B\/g` gO4ҡmm|<$k>;Xor iS.kDRxDgb[ܫxD-j}3àlUJe.NO7K?Yg5߻?$_D>L`o}g:OZo|4~'jpPמwq[Or%o5(AGqp|sWg⾪=-B(q)OWcJ.XTsT{NNml&@Kh6拣h{5{ ~cʬ-j n>G9g#`whyj>i{bJcǢWr[ F* nsF?Qb7|{y巵6JtXόga]H|6EJWre}FY(ơF#㙮{KJF{]&;D}oN&뚹&D4ldsjN Lirci܇ܥTf4l$|>CTVv3U PY=ZkZ-" $"F5h`U [/1ƹ>7F ^ԓ6.1"ѧEbo'8v\(8"A<2b$l˙14w;E`{J*bo myhf>اϱI@%?eYh`|d+Rx_#%>^v?2m=ZNOw`zM ܧXl[VYUbLJSz̀4.y1CV 8UwrOWYԗc=Qt"A:W~1m7ǣE7ns/jS[}r3WR;7ԩr*NV8O =Vת'NQ8KL?ir'[aV8^ˣۻ@XJDEosVpj9M*tdwr;c6iO,n#n|6&t9Yi6qmSh#}锢6)MmS"Ium'uy;Qnm#Nok2S+y =fJ@ |vuϗXib<ُf>19,g추F/`bUYدC'f],lY| ハrJUX)p*zqoZ.vmy&lw/Un3~@5Tn cf<3<&9@nl*ǝ ,q33Y߱hXoQ.Mv?ޖE;U~nx8]*On  egeY_M$ 6CW6!LwY9_䶳8Ku7,O9KpYēYGF-kW%eu==? ;E JE^D@P,Al5 (@ňy %8jJBL FѸ<&Uy}Ns?ܾKݭnݺvLҶqy.əp׉#rN:10'6|9G,`|͉1vUṙkϱ?_؟ֶ؟rOOrS5,ν\U+Xke֑p'Px('IpS8B_B!V qlGSvBZ\ }(oYחnbIqS8()tm"_soja2TT?pQn=q}wYI3I\ if5k# ^%/n4eghHrϔr_|Β?sywn:>fwqW%( 睕ө;FqDץ pb?W2II//=0*>sq>zZpJ.;9mX,4{Og(խ7cYnі8O3'ys E;n=SL^ H^yG>ٟ۳V0ڳ nϏ(^pEP׶TDcI>lwSt=v4rYQ [?'|y{?^͒E =7| >3|7F6e ԶPC|<ӟiZ %E1ojc9B0*C C '}:Zپ:1[w;H&ZX_k}BLr?>AiJe\""җ_ݏ#~%/?wy,~iG^PIQsKiV5O3/2v+Le`}iJ#{ɻ|[L{L~l6x 6kr?k cM 3X]cua1eż7N!S3 (L!tI/PwO^<ߖ}w}#l|vhm`QIvK=1w٘_[2ekX#7*fy*ȴy9x’0GG ᅵG2Vځܫzwu6}}Ƚ`?#{7\~{\>ߧ`2̔!Ϛf <1rd3?1}qڍ/o?5xw ~pۅ~_Q]vs"_c-\=P{hĮKN6 /KIo\m'N{[js9jӌ39j7romt0XlpQl[qsH[41-ey{DZ% JSEiЧ_g3oȯ﷤/%y{b[z}*qEƾ:6 &_vХnX߈mz4~XOۦG\Ɔ=7_y,۾aJCշyQivvB]hH5yO'Of-RFVʲ@/7+?@`c"0~T'AN,r–S_K츔mRO76_ˉN=!J{4ͺ4+c W#0~׃03%{qGe˜$M3Z0~;1N6&y -ɰLC2q$E~"tYBGW+q7k2|څ9uFQB2FC>'[Bx Ůc In$_ g&P(ufC&S_OnMq^:Ibь{`%m~[k,GHΧ;&3^/fxnj+-c9{IǔfӐ2?+GXjF0e:IR 'xYnyzj"& Zp rX})նoR0ӂ2MeL;NN[.xH]O'F~+ ccɯMϲ|:$lN?]0N} <ձNlPZu,ܚuDҠnWgZ$'oh]Q3A\6=`jd55xp1QXX\b?.6j$'y?t;17qP*<~a]֟l"EHy.!ٛ-WD'YmDMzSNj9 ~S(M7o<@CVBv4ũb.ˤ-@/>C'k1Dž<d<оF{U:鷃n3y>}r/ݏnkC?zK ^s^'sm3# a|I3Ҽ9ue O/ sAW?ךr[ҝ!e^?vsQ^j\Y\+3FJv _}Vn>cfكO1dO2x 34x ZPOl'1֤k`< ~MǙ's1_onHFPzBǫ6gTp_Gp빋y^)Bh{AhY/s&򿰂!:@Wu9աCSڭ6xVmשW=iAYTy'nݍY0cR;s'g[hSZoڥe#/k&WG{CM齃4uFr}; *xU'Ts*h^wxx(Up}Ǡ{}l?i䟺*/w}0l%Ua_%Oϗbm; [KK{_Wўt}z?S k}S9+'~I O3GoϦܟ}q6⍨}fab~:L5ߧ2!k٧nT}67j?Mxv ~:'[S:=]u>/1]^c8W@v؜vXxvKSà;/;~,ZVsUzjjw~k0x'~?X/_>=|n_)~EeDzOމov{Mo ΂־?6þ|viJOk{5'_1~{ QRGbi)7 ~o&6zp?xC{wfCb'_ >pW|?:M}SHԶlL7+6cXhUr/5RmtHL=Wx׼!之 zpk[[Px°W(h|ԹgAy>80 N\;݄H^^G5ȟo9|_#{w Phh^^V~5~.2+?''kbqdxZyVjx؎T['HYm?;VS6@q6p QgWodpd~Xתւϋ+xy:ǧFmضWŏ!ӚcHu<6Q{83ȋ|Y Qk.#Qԃ[c4?Uހgq/z e]w3eؗVIg+' e$}2)I kSD`:Lp\׆}iHp{rF;U5&IH$m_{"I3?j^se:rL&}rN2d=cmx|$6D׹ '^(p114pǩuI#ܱV9=Y9;.dZSj>hkm֟eO*ˀLf!ݲ5<o3݌J u#/ceMop,²h1xWJ&ж]clym:?C?੶ :Q>ED +ZA3Mnw1n+X3.$Ųᛒg["M `XzMFnE&Un!H?B&0^1Ɏ:Q!!ZΡ\C~Ge|ŭI||#H=a.x=&~#r\.A>T&/03)B/#F4;Iw=x5gO0fxר[֤>0+_Bݞ87#x xsWkC5v1tfVwBjOѹQx̉R]`ewJ r szWo8F8SN;y1aql)|736Śö9:նE25ʕ B*0f1>J‚MնQɮSQg7fJ'ef2j1Afc,a iJ ,1<1abyc91Ѽq7|1jZy3'2] ڴd 63 aЦg2#}S]DnRNy}˾SEz>Lm04)HYC߽TQizku?}xlsgr%yF{*N9%hg1 yVD?oNox <\o|$M9h?oZ뀡tG\u>!8/=$͠OiHy{꟪պN ;~74L43i))i!]hy]:Ѽ#h9h>N42uLSʪ$T?O :yeP^IWiua529dh͙Dsn\4aI4$LYI4/Fgͻ3LI4?$f)ڰCRsGf <4sP;go$6;\NZVvf^ĸ HƐ9zrˀS|9|_?q{mSo$|gi;XE+"])= U~s|ȷ}ݣm1/ks̀W_c`^Ҏ^$#ecYhziflGvTxClG~$Av40.1uq# ke\Љmv1]*#/Y~q:{ʯt~UKD.bl5b+|.tTz&+9,BQߤMz/G{x/SU۳K tviwlT2o8|^,Ϳ6ě|#>B ;%5[!κ,mKْdi{go*v$Kdۄ*;Dei?#77&jsknٱW:zh\e\8>sA>EV<چ3rj\>(e3'/eh%Fܣf(|$;DL9Gŝ6}<4 t: =_=z={-gATO eL/;]m_v99͵/x{3CU=0Xz)6XbIKЎ2u,0_g Y})sP϶D9fL6߲in}o\6^67"glydٴ ttۜMKgX?pL#f4>;m_gڼd+23c+~7}U^vH]>W3nT/z/N*WH m!]!ʹWe>O9-:Jf+h'h+h~)ʥ8J= d&etTz,S6>6 G+DCmcEYe7G]`Sݞ@xT+c]E!-Y[s ~/iRGs׈\Rs͞KʝV>;0Wamw.ɡH{3cý/<<1 }jd9,nWj{2ڮY\-=3S:ȳvSgp͡fqs.w%݁cs^gimY$9t/UuŌ۸<:s1Vy]1ࣀg u zPzp6i= mz<3MLcL@ ,oO摮؉rC۹-]nK-Tx ׾njг5|YgL3&|gq[?n'zWklvm`|Ü`,KHX*!&|B( [TB:oRa880'",Cx u~UGc( K6@sm̹6v8= H'O- TdP@]dB/׻P.qZ!R Ig [B:죴V[^0;H`Nd\qMrsaC/BB=& sSqm"O8%-p -Ac׉o+/{r5]ӻ鄾/\sZU{Ll*mG;B}Niw>In'!ˁoRwZ_ά2@z+}⧔&Ez:9(ggq+q$di%1Gp $d8 \,pWhD:i9E<}ʶqUs藎"FNSsv*"=XTDzf}陝Eg Bh_%qVܝiWb~'hQLbhob`0m)bhn4ҧ׌㬚Oz_ 8D1w^*d{T[>ſ|$ g "JHU&%Dz .!ڇKvd)>h{Kv]鯷$c-~T8+ ߮紃;IVCWowVZ7Rm}qQ跀9@vDxW ||պC _J^)TO 87LS})זQeAN,]UFmcG#e6˩/>+>8'696(joǯe^Bv7|}9>ZN_N,'弇\θSAi*(mURAi};0^0L!$ MkG'qK CxB9Ӗ=zs}$~Cff):AU-FWHg rDKBhy4Ӟ{:GMZwΒ *6kM8`g%p}ٳڣߤޫ]HLw b݌ZJC5 ;8_\cMpc\7?^θ &T>;88Vxal=o(."YjgW 3o\Se3x }c?O8j,REe!5{O>{ f~5*9? &9<gu^ ?ͳɢ xߪfo_u[q6Sa'WAx8PЋ<ьQi3(jmNOw: V?$Yp3~n_s_)nYOݖkƝ/ac~K[:r7sOƐ4ftާBdWj &ɮ\M~!W_V-<ڥΗG3a׏GN%'"~pjK[l&ǃ㏌tHᮡ8h|k4ͫj4k4Me3SoEg7]l,IYmQgW>Lfh^սHlת;;1u%x"-t.yzh{j'_fPEo2q=Ժ]&{V>Z4e$`_9fd\&I @㽚ht-516l۽i/+t)?RYTׁx|=g<ReijIg8%v h㝀p=ֻiZ-k|sm0dp8KEHn4b/!au#:oa<x nԻXz'zNfpN=񃍮1y|%l[멿QG3gbxjoqYC{xއr8JOǥ *7x,qƻ=߂pq+I? QOw}~0Axt@{a{1x lL?Ĝ XlL{+ ~}x`uO1q 4&>2Oju(s^Ȥ!^B{0/ٓ^Q<k [#;6#wZq%frnns|x?9h__fƸ99}wC,0H`نp.(FyyNbo9yTMP_vO#.|#:>7 °= kR0'uaX/ 1 kaX Lh]?ZG+"lZw ǺsM8xsTG '#0Q:5uڅ}/GUN0=EƷY6y :C ƒ1C,, Ola?lrU>Uz.S|Ý,Gu:N0w5cO%6 8N#eQ4C$8e?".Td:Y~e"!B}mal2|Wv,QSGA$m3`4iT':Y)ѠSW6F\&etwp;.TLDt9}hs)aHH+dH$LO͠ݎm^s:AϪx.=3M~?b-f2ϊ/Oh^Rc\VQ )Ҝ!m-w!U.̖dzy,.hH#[N-Cl^۱ > YjkjJQ[{J CUO+fE3Ga!ůz>&PϔpcJ.YMΗmu\ C)L4 ) ~VϝFj_6fg.Qs_ZPuԨRv[H[sU^nc$͑Gj${6 |(kwΓ~lF_JtCoz[ ,氒wJoe'2h)soEOxOJ,$|Z0M[EF*wuk S;mױ7vO !g5*,eWyǨFk9l8{37GτG]觥GF~HY'lwvt4'b@g [bF4ݛ߭+Xd/#܃!gZTP12ׂ# ʋE.EY %<;VɄ:Xq>ERʷN=aY\rQ<<7>iG/iκ١vOz/::c+4֨C4}oĢ/qm^3zĮX;>r!K%66znJ9sO#K3ߑՎ9s&az3N p`-T[>\u^7z/v)@eYS\ ys)זzay>k =͉s'Y-"y-*nvyp-/jg?&ƀ2*n}-s=qzn>AƵ#RNr@0u|i\R|n3Vj&r3 *}Zo]HOz±*}GZ3ȉ~Ǧmqw?iRFε^f { ?h^aU^`3a?l{MDsqE"˵/w$b'bOx)OW)@].>+i_OcؼD}̾DL$Fe4ʆmA%3­DB8ȏ13ˀ$-+&Iڗu+YoRxM>\o;5's(bPsHI&< `'*Jx`Y k{x;S{gFhp7pk8$}&'ͺK9q,SwcNB{Im}Io&uH 9?rd>-RGi2򘗌v~Z3uužW_rc^5|{}CoMc^͑A3#4t\ ͷF'F C3NrOSExwV̳Sey (e82uA\̹amh}G[>Iꓳ>r^~G&xS&x ^|$ܛ ^egY͙sO&<b%,<,9/ y,BPOPeٺx-[gTZ]YMeqJطslsgxFBd[ 1&vxfqjʋQ{z C=5rd>|>vn7? _w;8U{UxNgk=[_7J 1E-wxٝ0YF=~d`73YM姪#NkcPj^5_eĢvPj'ghwQ-Aj)zI+9ڇ9z_Q0m:VZړRe\LZ;u>ǓS{kϬ+?|0t'5{܁u0TxS5UT}.`V3o<݅#+4ڤdS/>Hs;pJd>ŏ:σBS -&g5j/,ϯOsh m{*F-~r!Zaop o:L/-lizN4#fɂGNh Ymg8c l'x!Ӎ3b>S|Pɜ^MO⁹L{j>).gL1?ARu&ڲNwP+*WGbe.Z/R[[;T WǴN3ڮF[27?݅|S/O|4OvXZ]Xk PX m-Zh>' P(g* qrx!lO q7}#YUoP,S͏ZW+DY\/$IbNBYfNuW$9)4^ _-h#YQc|~m#{ i>;hg'Zzm<`?ʋm{L2|)w| i7Bbc#˙+|cqdo _=%O]E@?K +ߖV˱P][RA0K{SoS7;GI);4j2)(7|ߧ.C{&r;<6{|m,; |a7#B&{ l-*$E6zOBciszߦ6-,oX#<\h*lV U ِ\ ِSWT ]R:K,)γu^-|R:;oKR|oTm;{` *ýae8+T{sp We}^A 㖕uI׶Ca8R)M) WP8P 'Px5#(̧0 (,p)dl%)B޿B>)>N!3_CaQȟ*Ja9oQcv> +)?hkguc=u™F©S߼Pyiڪz< <PM21*SKH[\B{I'>Xp[zßWa#d \B|So1E:iljJaH23 ̴'Wyxv֤t'u/*C?jjj D+<,9ꑎ6[m;Kּ~Y&ZsC+:_4>pO.ؘ,gtuJ]~]9\ zU?Z Y7:Пb5j}BK.G6Vfu vYWJ6Ƅ굳묝wԚ[\RϚ{S5^5^/uxKPRghkY5ZNx.?=r|^;z{Pne] tEM6p!,1 ".FEfƠ' WHI'>tMQfנ>EtF V:y9?֭[Unݺ1k0Ti~yr˽+qgr;:Ҡu"_X%]u[{? 86ړܦ'85ڎI =z-B:&^EӸ74ƥJN :3N.N灑/NΌbSxqh!!qf^2~R9/pSj!kG=r=JF9@5VelZ9Y9ckR!+ogoswks/lĩ̉j'SN '}[ߛ}u@:x7s^䍌n-\;GAgs7<|Ya8q' 4 [u_Iryzy/S>U6mD{3M$3S^d|ς/lmQ) c9sp[&I`'CƓq.Ss2|q_nXF-zo6.6M=r7[GpǡAq1e e#Op*5_s8Nq}4c]i8)3*Y<F2,G2x ?Ge.qcx6 )Ysa[zi]xe<7ӿS%!~@q[%kxGPޡ(θ"N:k}Os/1ns kX4n#Y$?'qB3Nag0.\Ƹp=2A_d\Wϸʢq"}#").5 +Ǹ1˦1yK#Qд|&,[)g/chb9Υ[p^C̥uοsɎG>y~e{nͣu~|>pGq:cPVtaƱZ ߉C~w|{G?۾WƭO0 +$ ɟ`e!٦V? ɟ`G!ﮣݣQW䳈mb @c z X{VIAY(qtj#N=z VdZHbYPG#mPw4c琞ccFƨ/maïG8n1jz ùkxQ\RGe\QDչ8Hػd]\$sE2ǚ~YE1>euO>E;<-3?ߊ2AFdZ!tC1=$θɿgpƻ(cZֹwB=[z?ӯ6(f t g+~-3-g|}zʟ-RjFn-8'gi+&=ڢ}L ~Bs *QoB(yhNb[5/|yO^jJW:͝F~14pLW,7inpXW,}OQ:}nŲV)(gaEFݎK̄**]UBz=%׬(3X獺5?kk%O}888ر'-8(q,5hhGg "ST7IeP*t,.e$?\_Nr=z ޫ1m,!?tWtc#xsrR].}\9kq*'x`UY zgD\A,`/~j񭭂9+HVzV:JZZI{+cMj[60hwB}4V-kJ=Pqn+DוϤh]E"ړ$.'UQ^UsGb49****]M򪩼[ՔՔWz5UZMy)vs^9oS{OZ_eI /rKM3iyhh=Q~DkB-њXK^K>PKkj DZpmGEJE}2ң^{{׉M~0qq:Y_'>.eO|uq>0}w5'h{[mlk88ߢ*:珶OX#6Qke~Vbf _+> &>Ճ|3tPC*Ƴ'<:p<㥀; Fts>f6ޭ3mYKV>+וx#=}kɖ-|^н^y'|{gG6PV?G;3%3ILsбp~WOw#ͦ^(My漢1<koK/|zӦM&d,GPHc I148TOz}?tlʰ*cOL1ɴsYA,ۙDL YL Bu<5VƽA C3f < t427JNkc]^YC Q~ ;TXmJ?|Iq|ֳȭ;W={%Lq4,b%PJ8:7o?$sqCh{ gW#m]^gc.)hƈ~5w׏g{~c?}C"%}B:1E,1QXw9džLn/<ӧ (v]zvGQnWQJvkv\nO ?#4&2pc¢{ͽ#/3myB8(7rg11bE̷Il:ZOٷ[]'n](;UZe~o2KÛhBW@I8UOKߦymqo6i}ͻ&9ǀ >uC;bDi?DeA75 i &M3&/;]܂vd/kT`)?MbGcjqx~$ݥŒl&qynh:AcfQ\31ڤ'>w*?GGY8 s"{hX !cgBWe(u[]0|nJ4QxD=LΨomvohFH=k~Ç\w쀲1)1|W98KfNma;fFjK]KYbQ⽢rWNok3Wפ(.M~@|[3_ n!~tA~XGx ʼcњY;PΏiX`@k|x1>-ٶ:Zxt4&_$ h6{Il#d:H$7 fomA9.yYA9wdL-A??rԑǷwD-] AM m Dy1\]}b0<\f~ EkBuEZM%Z(.ҍs:BhغҜ}aqokfQAl[{-Wxl Cʳo%- Άe\ue7Ps# E-A&c=1i xko 2 n6=jl%X{s =Ӗor}Qg/[D~ :zKy4 ncW Яewlba;~{\jIĬ6E1F^a:B6[N{]v<]0yg9h;. 2A;]WƸ1۾!NwdleXr֕XYeMo%ò 1FÚ书y`6؆<_֮Nw5c->T("*em4$_b[J$d죘5}B F)hz_χs|Ϲ{<}~yZ64CLE~L&&=4:Gç=sОm{!Z~@V* qҍݾ=v)W6:SE.FTL Q^Y׺RuZ}:=OO1kSVW)PO+),$#4hC3?I]~0L^} ֘S4!+o~G'D""=YCKsNxܜ0Cqoԟc9G]<ֹ8x> (lUiUr}ids:ؒts '7 }&Q]F.MeӊH[L:$k{(=gKNsiz.|6Mυ\t=#]|g^t`4_2ʧUݵm'2={;U,^Is3G/12>_#tݗ ~EκMaYWO^,枂`b[xn1Eg\z` 0gg^vpN~?]ts}`b1><#歱-̙˾-{R䈰tB(gZ پ 3fgh-$C ݮq>X*S먔3/?KsmwܟƩX5ŘgK[Fc/t@11)$"[Ig|22SujgZ'f~rSA~?ȿYQ^>ߑ'fi^Ys.Ϧp**PM;ufSDa6CMHr-¶^ՈV44xWJLW DGe_Wyzʒ?EByߜ:2=Hז/CC#r[zRo @iO 5W?Fm( k[CUU蹞d D:E.V=0oe}_e4| gm {:; =5xBPǮGxe.=Om }Ʈ:ۉ=@o=қ!Bcc|0y] =GS?i@z:ՙNt;qH#%biX_jn \|/A,#;NP>mh:1cwj|_#^->ASM팝C=K:7šl}@_.Ã߂wie5$"Cr846p3Ϡ;PG3X{L+4p1)K{}^Ic_vq]bt#k/+3(yjg;Yv rYF+Om .])ߔ(zZvp<礞\S_ɐ!zE?=AGg<85ڲB1~֔WF_-?O𹽞_PCm K?*OL*{8U`[x[՞bJ5~c-&:ߋ)K;ޛ΋S=.9JJƆ.ՙ3.?,nnmpEs 3Y)/tjj4I~`u\H5'&ֿ3&7G!:ɁFVgkbzI6uh}ouȴ%!mMynk"?:D?%B;_qKDۻɧ2YTZ7.[c"WrLJrHWGicfnXOm,s7sI{RY,Hvȯҗբ~sr>"@=5Yq-JbƮ:o%K+; y$?M#QALW"nM9Sq\!gndY"*uf96hֳq-ӿO^䧖з)"^Otr7>_ID}h]*v/y{o зƊiW9*ބ߬vj/oF~8/29ymqܑhjdW+>׍w@hZȫiikS ]BD>{gy+;l(>6'l>x+>=c:&3ؐ؏4?ƭP \7r"#w`5˼~Ž(Ɵͩ"is/;M=dC.~̕#G LM<~iV]4Q+|Y/­WS(e7W*;{ LCTƾTxܵn.Yczw-p}ۂzN+}0 z*&FopY6uqCk_t%'r|MYfpIfH~J+.=N_Sʝ\+56>`F@KCqỏ&ǣ^{t obr݀^s '#aׂ7$F/13KG>i}>|}o={W$zrMEeX[*տ4qxgj2^U|o{.W rsHz\ݏ>(>{2[ug;t@UNPn9Guܿ"4 jiϫ1de}aK?"6m~6| Ar}i~EvN> 㜼@'@OCMx d{u(QZ ߍOtmzS6Y~׎Wߛ 6JwKP)+>/puy|n~lßx(X㟍#,p,;$1{Հ{N {Yy>Vƞ%-j;-bp{ t&LۆiSM!5Jی&o'[ cp ݅b4_s QnsSwJA)gwFCO- m照G?_Ix+~frBj*~wp"xZOYd JO@?H;nwPC*.&-7S[{ΐê+~G!M3ΜM^FVzBRZF|SHn&$rKcDr_'6 >s`Gr׭Ue>u٨)9m˼o2EV1z )/brΫke"i}ӻO'zGM+2>?x.' \ 'SmW${Vn81k*>=[~oK>BjaWB=6>W9DYm.t~|?5Dw1u=M4X/uu5 zn>SxߠWk o-~&^fQ/e0+[iw#YT?B -V9w4jOcajwxT2e~ݏ{NӸ< 0>hW }nTة ָ}ױ<ǙN'z-Cw|>x48tW mn9G 9u~3Q= m!eU׸c9S'w_ nHoID&;:pǬz;7Hǩ+?z?aՋѻ5HљAȝ,n3[>LMP >W'5ޘCi? E Gf~u)pݱ>c a#A7C76~v9wD.06)DO_ok~(z\pY ;G͌O<.q?X Xϖob\|̣Z_*b9u?~W-7qm/z+6=2\~w5UV9&fW^36,*DW.*ukP}Nt8hڎ<]Kr{E)kXM1m0U,ɓe^l+AQPA?k Vx`<'L>gSK&dnruHnHO|a/ *O"C|SOqhvѧTj }f{cϓ$k2= N^Ow.h֓jR$}SNuxǘ%ƙ6x$|MlfB oL:C>˞Z$y/'Շ<LgpZ~6|rg3l0Ky-қ4e)xvTTy5]}fe5mL<)ul1c8Xe1t;H牴EdnGݟqΔ-/%ZKK'lŨic9Smr 9~i߯rɸo%K7j'\~*{ApO0#PTmFfKic|.=O{ bS;2z?B|N;3ߧ({:O-߸WE=]fϝז|0ci^r}z*Hn͋S|+_cFPOԯ+#ojIZW鮏BZ c)Ø5JۼmRu.ת v _U?*4}deV*D~qWCMBdFܮCWq&et`hxBZ$Kll%IOB%ĔQDL)[앬YJcIØo9;Osu}ugϽ>־ Şl(;}iX@u!z4dF:dK*%Jz K'z1_XPUqn~JXw$2~_kjO9:pٮ+Ky!o-p.59+9~C\sx^'pTcMsϰaÞ|rX >}dܿ?ʳQ˓gZfYo^~K߸1oWajlgq;sgKQ,\p Lsύm:&<}K?sgRx )%Wa6ߘ{;m^ l /GC}3oIO(>޾)? |j8wXcEydeM2&@oG{<.֜I>%ߏ> H;Io g)pzYכ~摬GӊMqw߷u^y} @M:/-/ |b^<}lد8nƱ=OWҰGEzwS{Fw56]%놋+s'x/gEߕyDŽ_hq(e*xxwm!b%|pw_ nr8ޟFq۾ChP5Hmt(}$0U}'e;)xm~w(׋q#o)}LD^Uoq=uj! u:I["lk_*7hFi>*O5K{FF:nBe {ٮGKxM ަ*rmY?Fyyo={[x;|oPl~k/WnjtVS{p=žnE!U4{/uγΈ'+#cN>|Bt} <_|տhy0U!f;i^"}#p]TRe"2ur|K _A+_m=2[yjr yٰ;Je  ƹTjoV[<;c+#S*gu&2 =WRKxvN^Gv'nH3qK\bM\/n-\ _P~~~yoyoyE#]uOZ:2XNlةw+>Rg_yu1c,'_k'aGo#7JuEٛWK}:W >I/>}V:Oa?t~ zfz>Om;>=*]iChNg3, .ό:v7h %e;? F o%y=߲2p_=ɜa aa:cS4m7?24wTw ӎ/4C}CWU= ƸXs_j_8YTY ޷F8*(|k})-7i\,1[p+ Nȼ$M{jw)6yp擙2nوӇ:WX/=xA7(՟No%_8M>(>obدyY>ymKOv Sy}Aʰ2zR}3M o㳝q͸ o5|45xJǹd~hQ7&,9~ {Mr5 +'oL}%piny= \pk pG ĹkGc4-3Iq05ɿ֓C{w-?r.,#|CzCO26ߟ*R:ְr_3VYء|E'M_%5o>AuH{}3syc .Я9g?u Ō9F+]O{can6xu ƏU=x|҅ p<|e(.޼2^cÞsz w5ż= C/, =bEsw9 '͍px f(GM.nЂ|!٨s' GocħY<Ξ&  k}8̫^;w:~wqZdkf~qGoow}"m=[oh7|xT;>Fէwgy|=I[[K}|ߺ"Fv2i^/>C2d$O^ݰ?nH浞 ^vT{ڂa_Rb~yNsNe7a'Fu.x!x u?FsYEMnQfJNgj ʷy(,wpJМil #_Xiž=RU_r|F píq)܃,voշ>(}g%鱸OOdȎe6=㡀kTO㽫t.-!{p.?ڗG?ǨOHnL6܈׎O1_V +Lwoa6RFoq;HéLi[dǵFkv2w޻ܯpT.1E볖j/3ewr4{dFgkog{k7mCŻ|qwWQ^xo}ͷӐk,F^OTJзGɨ~v w_wBj5>|o~+Hkzacbw,VkxK {6y:; x/7݆mFs-pnp{ \ K~}FՑ_ǹ %>e*G?`+NW H{#4]K]2RTW L4^,<Wpcsq#& W̻/'3c ᮂ/›|!#^L|9]Mp4Er'Exi8r O_ԧ\0/fyk@A͢x+7.[<(]X$s+0uX \i<+c} KZ|ykׁWí~EƛO"ܿhQ/~YVPO4V.J;xw\#Ok^(&NAO4/%ѹ!ほ7 p"ygsFsNc\Mgr9H>/lgǙ;?^6/:1v< \= {)-gOr.=y3Ex1רvm7%Avyf/6^yrWRHnX}a>C̿ij{ e d a׭sO4|w1WfFHKQ.eN>0e~GZyQ1M%@ Y icmϠ<\dʒ-x'5A&)kG,!@_;#T_a'$*A/ɇ;0GM'xwO# {FS^ڒޚM ߻ɨ^~"k'>Wy3Sgّ;W@F_׃wˇSq32x/t߸qzNɿ J=}to*-+d^cJ=k F}+|}pn^[2zsS?mwU{ͨ ƍ-("q8oGӊr)xމZ)2n"Hgffot\t^c*x#}ᵹ9$^n'{ϙ!Bn/zQFvdt<#~e!Yy"7W\[?sR1 <׏Ia1`c +܇F=h_| yr=LcL>2?\% 59.x0x.rK)*ȧ& l#<#M mB߁kX,ix9|-MWKhH2:Noet~6˗\0Hblx-k_bK@p !{^ptTߪ2?R|eμ+|SeUVzkwdŲ`kt^%)#\HGM _=fF{]Y7fz6) zx}Xw ұ5#Mc}ac|?3o3;ջpj^ֿj5p ]&| !n)T&A~G?ɹ~O Wk^{ey'Ν>a|$\Q|һ?f{-R.^|S$^O7ʽ'TeNv*O/FҨuK{9pYR N7P\ʸLְSi;Ï1uWY,MN I!H i E:•JDiR("MP W)b! t ~"M ((Eqggs?}:L | [ sï Wx5A띯n''ŏx= >:X |o |ҩI6tx]|I%LcRVr)|Ga \~}g!Fmc̀{yU޹59M`z_?|`<|yv3UY!WqZW/׽Izkzԇ^>aCV qqe#-GH9o xk^9KM\ _:H;uJ: i EN(\< xG?>Ŀ8aw(t#pC<61/Ԗ3l65'M U=p7# 3 Qp2G=pmɚu_К߷Yҹa g^tɬMF=^ouݔ;Hs?"d8yZ ǚc [^eܼpO5l{)w+$7vi߿tSW6}%BkC|{ U?qE* o!}c3A. 'Zw>gu鉐*O2vncccPqhK1sMM& : GsUr "ASdui{g|á3+7"bH_;uO6R> L1~YSȝތoyֿYfves-SmYpS!o$}=*gGY^܀z ?|j-GxN[k+MXPH>yGYgvHchz?ϝ:43\|:\{߹e;tFs) |nYy_=N˭6+Vo11пvRK%M| TwG}q?bG'~GwցN41l3/7f5{gWwd\9^:}gdV¿p AG SV>`%o!&'>^oSG8=cy68v ΢BY^ 9} V9X{R EGN]87F_1#ly]d`nxa]uZʲ5P|8:j&>5JòhޏH׎&ǭ=*- :g)~?c |5սm2'\`9>nc ^w=؜}iLY?)'cm޻+;WQIa]7ca]bmr])-NBA/~0CA/C[ṅlY&瞬S6.mQ+ZۂM<.n^8ecІ2иR-C(l:RW !K _j!~CL+km/#q/g!Wl |8;Jޜh};{N>HL+O]ig1>4Gc}9%٘w˸fNܷ9(pYivls7!|`׹벜|p/*7HYt9)T{ 79on6tZE|p}?- zkX#Q>sσֱkcҕ2)]W/d˭4Sz,so=!\kތx" n•w}|]7Wv2φЭ!t%9Vܶ;aTPԊWnESz9i߀~S;GDY?\Ko>/[m}?sI?\n%?B|pM|O c߷oa>_V{^Zoξ@|[[:YB|ìn#@ҟkn/\_^=m໾dv pC[ak/MBZ@. >m?Du%PNX"YeuDl.͌M̓/Mph NtD޶Cz9u)^з֗oZ?!kʿ{=y w  ٗF#J7Qq+mi>~_:zmޢwhccͳ!6nȓrnNM[:S[~Ģ,8;#,2z]NxV{YZnBٝ&-{d~?\A\߹xOZV9 {] {*?.KȖȟ>iQθOm?CT<%.wYPѥEOOwc IϦ=l_uB})k#Dy0NO#q&=r#f%kNCcY z؜~yhu9s -'h d-T ^q2p1Su $oHA\\8धWI x"(ie/SG -bY|/D.|)=s؏ ϊtKpye<>+g~V|vW|\/W3WpE A2ptv,Jxu>gj:?űî? q#Ku]?KJV\yM>c}/2-:O^Ӣ-!>2k9ӪQPӘ qp\|Zu!^h$[g@o P:xz/$#!9yrn'[_֤P66ey=_aZYz^r#n;]\Yn.%up01qѣmR#; ˃/ZP2o3͞CspAЫ֡zs-{Xvvbզ͍^w|߂|&)wUw \E>\wy?>'K6mwdfxLw \+^bEFm2 PJa# G?8y`V"a+~<˗SAI-ED F;2y_jkK8r9=h|?Xz*lei~r,x;T(S?i;Ϝ$(e}af,kA_ERٙUd=AScOAi')^yLiS]bW>;}˧\;U5Пq\ken tZi4eg7Lxam 9rO&߈&!̄,Flr5#ҼH}T&Dx|%uC t &Bs H%,+ o(5Udș*u]+gw矞VIsy-TީQ< ݖ^%T>c\o M jwɷ81o {~αK4ڏUϩxV}B>s0ȩad/:&r_7GVd3׶:? сJow>mB*o+|3໖/0ޗ"lcJXq^.U7H !%8/jǁ#xS03F̏_zPv?J9ppN;)jp 7/%t]R+?)gޡ;8O}'-c+"MOptU~Xv~WPN_ G?=c$b*ruDz",SưWjU.wq(&JH6s a[%n2|J)2a@2xȰ!5҃m2*9eZl@F UMUqp:R;&y%g:p^N,yÖ́/Im͑2ٔ$sԆ"}cutT;&|wڷ1nFmWi]>k6 *}5wrbŬ23spCI)[ftk=:ӗlV6_iP%߫Q'F =m vN,.kG<׎x|6GQy yG CqgZ!߯ھO|e]yS x}U_sA9Kئ=[cwj^y:[Y˘~-:3%'M-mQvj);$JPVDՒ婊*Z+Z)UUKSSKs1_~7=Ν;w{gD>]S1`]P}^]??CW}DoU)7eTR黐p=;2fnmm%dZ5UzXuj~UϪ钅zƮg1H>։nUgxL/p@[y u/5.ݛpnq]lԵL}wS??v-BRJh#>XW¼{:'yyJ,\b%D6-[磔|onZ#BFݫCc~q9|@GtֹuH!I󩹒 kI ;Kg|jnSnpx|-&GTIO짐}0C0m!> *uo(y+-pZw׉<~B '9^ғ%}u|ulI0 uBuTt ]:ltGHNnQ6:ޏ[KډA΃7TF/?\neYDms+.Vӭ[Cˬrkpr;?ZlU>gSw$ ˯~;T?-v .:3r ~#lحE}m@N{T_xN[\hN7fs-#MVϴ<Iu~liEWlBvW;s}o~*T`=>q~_0ǜoϥv8?Yr?9%(2eiH#{<Ӻ]p?ZG*\!SzO >l0o^( R=gYN鏐00zGH18FKy'uQ;>3ԥpD cڽ] ; u/94xcB>fQρ'opn'}OwáߩPxl7+{n] t`}J6 %`)Cϣ͢{3I_ߒ~-XΈ]{`ݾ[3E+&Gi&>(L%ܢǜK/aw[H])_7ՇXZsX@uHz9J.7·&\뙢Heus+ uՇ>X~VV~z@ۍg\*5*=ؿa7:t;+\Iː8 *{q|Tv{kLZuVyԻ@ĪvLTہ_#Gv˕Ij,ergjvzN#_s ]wwG bS&Ȳϭ[>_ԩoZ9ܼ=(?AwHs6(wM.uH׭L6s{Gҏ\z"\Ze&-{;nst/tzgryoӲOQ)v'vnHl \q6y5)략Uq:sױ^Q׿bw& b? ~bwr7ZgޞJ?^z1VsRs2w9\/eYk"_%_ (,u^u>|N:TSM]lNa5%LoA8uu+9c݁cǺtҎǐ?^8NP@Z\۵qu zN=(v-;W MI~s?PcuV{.:Xѻv̸0'Ia-y"~b!mZc Na. 7o6~|\~#'p9=@t9fuբP.YX 츍]<\ tǵ؅6X^eg_[>ڎ?Ty,ajh5 SD:,ͧ 9?*͢gӺ˛N:DW@tzn"5?ܬ3#Q=3:kt3Iϵu\c=ΖYgzOSX~\C?Mt_? ]1vZ_QŭzzD_WöbWz[>mL}Ykcey["-=ǐbe\>?3͢m0PKةTMmM~iX:Z@gJ>h|QtUEQثP3}[;f8(2RiO>:/bI>4\82y8 xN3lk(a_FK5 !_ hwM",ЬKpQ= 9|49_1+ufħf ħCK2\SA|'D̀!!mbk41!>x8$T] 4! .mu<xN s75p ƶ;۠y!s63|3'⣁;<x !&l;p5(}G^ xOssv&pl <x>|8(p%(8AO*įCb͉M!+ 4߁Жn<x:~UrQ]G/|8o+Õ<xY+/ ֆ nOk$ | W /\kF _< cu{߀mmc |8g[nklO |8W{E+G<]}=m. \%pP4 ρpގv4:*p_g l3\p 7!>O/\ 8 8.|]q̀;l|^~/.pc{V ? \- pHW8G ~ dA? t} 89xh>^ _/ <}Cf s% p!>vįoC|H_d4g|8WOzѠ ׃>8M0+a4 [' +^6 4pCz?@zt 31/|*phå|<8 x#1}mklM,'@|Ŀ+i~. ?~/OS;ڗuj&Taqϯbέ4G /~lh"w?=1JZ'sL`WJV"W"~{k>~Bae(}~N] ;wuS(3B;;4JHHwë>'7MQx0]*ic:JKEUy*lHpZ/m}MrC\ryO~}吿ZZ;5Ճ/߶(%v!?򜇧쿺覐90%}:C]..C]$π:zܱ/Īo3~ePw\z4mH 1 enuΎi쿤 m0eOoާk˵o7{مHvBB%{YB}!{%{%i;׫}|gΜ3G83Uَ|7ߥFqV==}¡y˻Gz4x&lч唏j#C|nqr9qI?/7C>}`7?EJYD8T+q')nK6}]o~HsѴ-\\GaLۖy}`yǂfo[M,oK7gM&< W9 K_dnS7(_;;9;,$>۴y(t^%E8;K q^Oǧy1SҶN9c5N߈+a.G@X|ߕw'q4#_9z-)g ;1RCC+pw%{1cQľs\^'ً} /{)' .J(675=x͏5~uO~}# I[8 JQyZ?kS郞F+n\f}tU~i>KNHs~GUc殼oKs>8bWG~ fX_sw\~ >D? [[G'\o- ]Snxh&?סԃ3Cfݕl^:n-h?u#v!{w3/}#ajTCioqͻoC0)rz!o ]:w{uڳUP'lqm xYHxR^qGW.cvQʎ]3|$>sEԇަw.u)H6e ϨKҦai疹&nR;i5=7O%P#c/ }z0/b+c!O'gz]Ĝ7,8i_X.9`:*nvZQfWЇ8u<ʖey~鰉)s}w:pD꨿Kٷ0t:uu W>=O9X"R*=U~<kwd=^)_ɼq>rad__֜(`_5#~d_ _g==誐X"pl¿3FV@y{}M͊1]-'\vq)̙~`sJ`?G֖=îc'qv4}\MSzkzc5p$9<t J$b|(E<]&N=)8\_m[Mj79XO+r'fAE^oeY 2'?SYAd-=Lckf ]ܜsYmYa߆KNo]{5UQ@ź[KNG:y%<ߞv3R&fRYe=(M]ryboQ XxP<[8/uh 7C'{Eݦ}3j;^2i͝Է5_qpNOgn1Wyv)5}G/W`_c6ޏx|k_$vm8.kZE/DxG\{a~8YGS0JC!J|{Lt"m+ߋIdMfhCzv5'I_un,솑lUk>s'F:㞬Je+HQ (Z0C:cQWUw['hk!*{!-+&8vn_XK5SB))Ki堟%}ܖߧjyA?C%]<5tӜo>gRn :bK~KhmoF[\5mpfٰ[%?RN#[:n<%s_^Nc2'_n-=^goGp3e˪X!6ׯwf/d_ ˭o_SCWvV)rbtkk{uLIԞ@f @tsIcIڔm/VUϾufL1u*{ܐFs@zxxp9,{%ռɳ0|xᔟ5?.gL-9nx?{t\ 8~a'I3݄_ *'j?[I?Ҽ9gD&3A M揫M9HFxgé}C|cwhS >j߅1g<{/۟U|jԷKBRY'@wE'-pE?Nzm6\/>7y=k=n vyOgW'z9hbJ"^%Nn 'g6|KUr>٣qO!{r]sI[*gʞJ%+'fqmcaj:> :tн/m6B_ʏ~8=sy~nwC_OzIqؕ![NN}nʓ]*RUS/춟x¾Jm9stI8:;sz8>5Uڨ86kY`WKybv }үiFi~öG6`?6}xR1mxRV/K}Hf8M.J6߅`igIs*®ޣ9/Oe}vxn#O:[r8}e1ƒW@Y@։a_2x&VϿѝi=ҬNp\AƾJogJ/Eɗ*fQ Ng_bgw Ͼt=ӷ}O']5'yGb^|n .K/{D̓Idμ2)^3ny7t t߯w3n0><#v%jQ vaN9ɻ. #hʓ& s{`yt-ǐOq6lD,Ah։K54{~sϒd.tH6/) ɘ!x/ !-Y) Ǐcgw?i*E7r7,g9e*"X['3@<2EyoAπnҽl=WAH勯shYo K|](mA9/W(~mCs[_崗OZ$WϠ[ۤN ^ܾh2/{Pc _ީ_n R6($(tHWeb]&ִ*͇Iղx1t?NӜXV5l~k|S\v"NW8aӹ^,+'#n3.pJ?ꤺy=םhoTq!EɤittC8Gv;c8tǔ.Gw4?EP{tǝy7Sz.s ПtiUvwؕ|gas͜x~^ %2-N$gO?OɇY+˿ 5Q礿gY,m\Q@>aw<Yk=3|R<7q*viw.C!S\fފp:9cPV7h˄?t!~=21蟐b'>_>9cso.MZf:"̣n]] 5҆-uqJ^q)ԡ׀)?u( Ҥ; ͤߋT?m.yߑrN NtKODr@_ѻ4/S. ~ʅc8%teϩ] ҽϻD/rRweqيD>lJz+EkR&<Ý.xN/}yY71.sWt[a_?t0MWǡo͢]O R3s y3'=H+ }2^We?Tm&Z߅GSNh'Ӿ_N@Vx^Yys?]{Uwm?K]{ur{I8t;x|AW=t:EEyޯ>? ]g'm:!fRU Aé_>#qr5̤v15?ll=C9v;9gOU%z>tS:ZWq%^{fWwoe&V%=l{撮I㭽3NoN-t wn@ t stR>R[1/iM]CxK] O삾2ܓYW)zwЯti(3No{=_H/5׮m\ϓx{z^K:ccmYB)n tIIםRZv:@O5_̼!i@mN2g3x/=nS|:uniӲ"θEaϽ;Śt{e,ze/s%&Jm +;o]FyV^ܡvόv? 'CJﻯ׍%}6Rě|O'?! gR$iWt>iMyCoAƿ8.;Xf'cG_S`SosF.ECO0\f񷠿ԣy݃! wi3죿/ 擛 x +?ּ-}gn!rXJIǛ ˥O}; 'eա%Ca]a4MxsSS{guӝYLᇫ-FS?Btʿ{"0Vsiݥ*eξ}cQfo9|%鶙FmJIHX21۝7mMd*a qG1S8Hx/!ś6'Cx)&lRc}Wؽ;y͟ݳO̺J,죝{n4iGf@NeˠbDҵ Ls[R(ɂp?Ñ_?s"Q雨/+ϗA8Tyu~w]~o.Ҁ6XN% >xrA[!^d7Po^;b ؿ{}&48ԅ D+ CMwZT@%7PKs#xQן {@W@Iwu%B-{uO+uEUji.#tŜ2ّ1cIpDeO-~p߂]I hg__Zю}h䛯!Ѭ:ܫ+Cv&,Q*zޤBކNN劳߶7JMK|îɳ}No!нt^Dz4誑{ÔߞM{oRG zߴ ~Mx:ieQtygDr&Np:y.=t:c{1^wpsvXAV4 t>] o]#%ke"t K᾽/k&N{{kTS1amh IS ^)$^T.й5X{!K8 viR&}+L[{7[|]'G ka9<3c{V!<^Mc b'c'#;:J(e ]g亮۽]J[Q!Cr+OWgWwʅYo侐e5{8w1wc;&mB'{.Z#s%SNًǒu6)9藣vx~/{ylOJS֛V3 wk& } r4Ty?n8IeE{Ƚǡb{xܛBW$ xR ~/ҷwz š7_%:Z깂/9$g/Ha7YAve*./^𯻦.ⷬOw-z7tzL;?wc jn}_~t N_5f:7pړOٌo@&xpt4r\JM5ߙtJoH$ؙf]gߒ~޴e'|ðvkJ37 oT3N>nSf#FRN7M*o9TxS5{ٍ4Nu q:,[Ixzk/`c;:?Pvngp[Ҙ|Kz;!y)lRvfS7Gc<}xgL0Ͱs|>-g}njBjZGb R>*[G;F%py @-Нp5TRYSjm z8*53lj`g5yW~8N'27>g|^2IgD^@w1 rCwIü-쳝.Wq`o9ȿXާNåff/Qw9&ʼnԋ1 ]<%b}v3xEϲ#-hz)>GVSFXY{;~og)ߢzn ~䶇.`M?ޥޚ9#<ޣ?AЇy\֗}e]ݧeC䖦p`c]l^h9; fAoz4VCB`3u;O2-a9m-[ӄ'OA- tIf6~ك8JA n]4tnN|͜viTuyozyl(>g;A}?̜5=Ⱦ>4.٭KOyf?<3y,xQqol2cZ5砰*mgŃi̙ED|B|F8m83hj$t^ǃr\ć)oSG3\/NMkw$.H8#Spf RwL~9$ˋ,W^d5-z`sS|&>2qY[Q"1ǔ&^lGq9p5ⶊ)^Ć{MNR3\x#au+|ٗYM)*nxW|T-p军nN:¾ FcPpk]ni% ܁Hpgį$xGp?J<ωӭ\ <8xǻ%'=VY. A%qx4옹FW]>z'-u% 3'6Y7m`J].`<$u=F7yhmo{Rģ刿&>Sv23wp~P|^sput; ~ ڜ#R|܀rz.馮OVAi΂4 7M7@܂x0%,pkĿw NpGWTQ3i7ܕS܃+p>w~4܏8)q p,q=ş("B<xq^_VPq_h{PxqpSn p.'G2imOOq ?,Iy "x|;5NXў|(ߦ^kqX4N6W˯*Ea~~Hקs;?v~aω P?/G'!NJ89q#S85q3pZģ3(6ʻ'k=wm K7P׻) } ;޽`ǷTm sz'#> O뀏;#|qn`~?>a^[ >C׋q?_ n x2$Un1u7ol.qQmڊ?#S'ijV_">ػf9⊊ۀ?fÜNCQ]8q3B\-箊ǀcWVĹN[7! Kl,W{`cţ}:3K*KP;_<;f}I47;f0&o0-ߧV.l/ cWs<3caK3J섐%d"l)'$,V7o|ɖ||m"59sk^?>ssϽv+ 6 <+gx@C})1Yi{=˙F>Ӵ_2 伏jIne Nq|w) ?'N5-LsdmTĢw=rw̳a372s(l< ݏ{r{%NcƙjrutӀe$wŔ߅wq`I1~70]6,W{C>\ V{@#1eß 7ү6h{Wss^g~$A됱3燑-qG?'#rݖ/7|<2#(h?_d)}~3Tڼh> {H߇$g |i?a5-Q}7u[M{g+.cuNi&=I{Jtem%:z78sqtp^(p9}_7N&3~3ouꫦ1@jB ٖR4;&q=߄Vԋ{-^q=&1* *iF'3/,9\P}\?n1 P[$n6}%-v,9pz_H@p;rg2j^]Gby` ,' >}*6P\8]# 䬲tRד+;`oM3/,-!gUoSoptg)^ ޶QG,zc{'zO8R|yIzRc1pl?Sx wI Wɋ_+I+2觉ʒ-D2L>YL fEݓɻʳ)LOcUfIo!\ v8H3 \l쓾k_t] S6ZfՁjM;]~woy߷FmuwSiKwՐiMq+L-O$.?6+(\:AgϾ6M?]eo,37==Q8,UrJf/=E|q}]հSf?|1(䭀|5-\^ yc\mGt SSWz_+>KvJX Vr /mxE 9f?}KCЛZrl~'\sKO83ngo/ʹ䤃jE!ML-ZM#>\,{Fzs>?PڅK&=}nS{?ys2:0wLśϊΟodn皈=Z ׷>7\M\|3C)t韶x/2 O(-vn;]C쟅5>ձ;owi9r33-;Eږu/Cf57|=do z/oq$5~~/G㔕c#uu_mp9[٘Mo?RL|_N$zյ}+pO8eFڳ}?^_j>dx3̂6- kMr^P199ApOW,SwS׺{,¦^}P k/|ՙ}?#{_#g#^m y;TG|~r#Ä X^=>E`?{iSu^K0>.n`ij?9٣?)|F?%ޕW[0zkAՙ#+=.QӭeohRڶv"3-;oL18o=+󍦖33l}o߭*Js,]z;9w@S'O̽q>|n!/ <FNNǘN}d\|Gou -pړp1@ɇu>4Wk/m߇Kd⟡={̘ρ] ~@ QW;KC#e},:r3Ml'ԏ4}'JxuO6"| r$ay@ڙr=fD~'(LCnT}w8s^ W.|n՞v|I}UK|u>C8%0͹ҖI׷;i:wlxịVmX|Iw/ꓪ/R r;}[uI+}ai_@or;w;g$)d)7*l?~-x'Ja T ܄Nӎ1cL-c}/+s}P߮M]ZK_#{wf1.?^Mmyۇi)^߄~Kߜ4v)٦jCIX\g_t=w7MeG׮|,=%O[#{h:(]=kĠY]?wA_ |(;:?E?nݥ2|m_J'.l!]˜Eߎ7?l[y!'1oV'e׳ak{͐FrwWNZq[oNuR=Cb2_ySm :\+mD _aZ3)7۟GJșgo9&Xir䩀W|S\\;r9?2 v4"-jL7\KKזkA/0];^߆ FDuYyQg\>Qw+ϭި߳_3wue,J1>1$}'Mq΄o9z11G~ፇx|]IB~?](OjM3.1AJܳNױD)TXp,<,+.RK!ItX|Tck2uTM䚔wOw';nJh1D:Q_?Yq ;go?olysa?UV7i]+3f9\Ag]o܌8YY1tCRxu®DBQșcn!sΟ7Ϫ;8&xk%**hXSO+P/QwqQAk 9 tkՠu@ro3}19 gc{k : (>TT; v(65ØN<[S g<_hVW7j%l&wD7ZIV<ۊjſ'x/jYV\ #a/z^bp8c7 軉ueN19~>&L_{W=D/6>}ѷ|>T4_F<{س;`Nnt,|3]1*ǭ!tSw)E S c+^Hu餏.p-k#,ؘ&3ӵΡroS1pM* Xry9̡֟Y%4ۘ=p~}yo}fbͣD7}9n:H.wLy* x]ߞlPxsggy/VUᝈ^҇rb_u濔]{U3`aH.3%-{(4I>CBHM]$ʯWQ[Dr)Q M]쳝9]{볟콞%5{> C_lPo,g8H}?J{~9$ 5rU{>=7:{w37Xm$񄧲\ , crZ9yҴOP+Sxŷ-k6o?{ :sY:x˭ߺwraΠ4]t;)qm/9=ͫT]s$ݭt:\N=F׫|`yF#7f1 zY ~,psn̯g{^asһ|_WDOEd_c ^-vy=YwqC{|FIy։|G[LKe8i#o?^]jE6<"YB6V {|`skf(2ν#%"wS;E?(U |Ǯ{ʼ}rVp("B(ßƔrpg\sTrp7&I2ol[V䈜 ;}ǖp>gTA`fv7㿞un`juuKy]fy4jynB{/>urxL~r>[~ wX?-`@b-}`uO>3 uv0ǾGbpxSq Ʒx|ڽv`!VtܰX֑x_zx3dE߷ \/MH|<'s^(n( SSj'Yzt|+=]^S[`g9')?n"2|࿏W?+7"?^XҹYk~4~/ǿ]W8qǔ`Fzdw9+v~[R++H_׷ws?\!{RKb&(?_սYK]w. .ٞت҇ 믑<|vW,K{/ަv'Vk$"*k {>%ntRY;{# Y:!vs&׏:K;MH0kn0f0=sy8l6%5ǧ,t9_p8#t""ҿ?L4/5-\< 8_DxWzxK^W<8 E;_ғPJy@EFs/N&peZWZ7 t^+,cc\x9_\6xWsd^xzi6Y~W_S_fs̤Fή㎤H_߲Mdȳ ~ü'`zsͮ=yڙ;=c4\ ^CO-~\w s.Oϧ|V>W"qk#wv{ӵ\M<綕^!׌xm_˷_.^+2tߚgOcO;մHzS|o)ט\bT3)wggS[gh. \gOY4٘CczG>}9r&XzʇI>.jނD5-1KυLfcV kI.6O>p<庵>|2' xdGPBKg{KĿ_|_wΟKƣ8~(/gNO~~SOϸ]޵bo99vG@׷C:Q `Ot2Mɏ')nxSUs45-='1/7x׵N͟>ghcd}o?㧋y]r x<ҋ l{[`9 B =H~&P9of؟ɫaOuc}c?G/K] Y)BؙO?4w = R}zw52Ǔ)|KCvԇ'NM\g Y}q3*\?߯gOybKuūdQ~<_ TSp)Zks?Ozzю<z/MclS~Jt8:]ςZLy0 ^[OxiXor$ʓ/MgBNr}&N`9K?3oBrt 5@:cƹ}M`n)}Nw}gߋDC*Y? ^cbY \mKoo5%JcP-[.s\-?)xn rn=Ni]Wxg ]V" gIX퀟^|4G+wKٵ 1P6WɃ \]DzH \gdnu^{4>rS܆utƀ_˓/c.pu}}'-~v,p W[JUD׫r1Ma lL~_]6~>^$z6d?nSxH8@XΒٰFA:=eWk jn9{+rZ{ڲ"1#W slAtY ט-㟛ڿ,=pZ5HՆisp8j_wT%ev:ÎvvD wyka޸7(\T8C&Grx|T,O.vnE‘rS^&xwpIcpdǩϺ`O9ҟSTox(o?ߊNb1o5ȕ]emv g?hX]zO5B܌tB(ǿ{.>gXboQ$iŷ]u:샐n44N5`nx^C4L}]"P2Ȃ﷾0v.Atߓn݌4UNf|#2WcBAGeZp.s!F?\+2"R!=Sb9y /1_6%mVh~uog!}{>W*JiN~şE:~r9'#?y ;PH-Hu!K=#.ȷcMڞRNNuKJnҤ |ˣ5Jf.fLfK; |&K'TRvv.8Xo~u/V_%#^B79Z/'Z'Rm<5-w[Cq{QOq9ߣ(k\{+ڤXj?x>D5~W‡{e ;;?P5% n+I_AmҚzt+ːnVkSt4(=Nq_5*I_AZEi}W/-kyE=i9JRJ2N7/|}eȺL *g3(9Ut xߑypppAG)]Uͯ=^Hyo*iZ_<׶WE(|Nnڧw^#tMͺw; 5 (xlEF2Ncyc{Τopm!H\q=&5Hz=q(OA#wIKP]{Ak']Խ_^g$>|7X<{Zzc*ӳ]u{̓^s>1}}ʼnv#e].__Ŀ#x"__6@}_n~t,}r߿5ǵ'=>T8o۟ڑ@FzS>82+,S.*=?ܡ? *ǖk]yh7?c(!:owGcuGR?9,ωs&~ 8?)>4 sf~C;~uSN f4ӤkgE4- 1L78agH]ʑE{,'Hjc/}9ٶ~8 [19_$2cv]1/LF>V&d| _/}C~di!oǭ|F4rWJ}=ਾ% q u \Ljp~O^hZǸ<#YCo.}twWJyxCKImÄȤ8;L:us)u:{gCP Fϕ\C~T/w[|5=4K~(OԍIW/$2Z:~ UA]EYo؇L~5仇DJ<Nh/:^Mz>fQa Ϥ*ą5F֯5|lx 8>)A U?a1~Qm9_o|T}ݞDOy=~״ zO>TfDl*wk@3G[WAe7[0B*ѫu TL#yA@_/W{1|*5iUZ2b_'Q]WK{W)"C;[3wrO7 <x*JG/ \bPO (D|=pkh1?> 3pEg |Ob7n  <8 xa 9>0\7x?p&% 8xd[ kv^y!KxG,+Ŀ)5&*}'\Q\yFE~Ojx_^' ~ÖNMg_&<;<}dޢi4(`>ϰk/[>Y:t@'<\:[I7ڟy*wϚA9"?۳H/Ѯ9%Hh߻MۗX||o45#2oƑ~:7_=,ә_%ˉ%-He+3H&}dUeo)k-s\>&?BKoz:ΡAYj>}u yJz&md]}ml[_HsSuv=mℽ/N~lR8RZu"p~T/5*s ׽6bJۋBUS/nyYj6s2*QN+i#:aY ~%=7xo5Ru)e=y4͋7Ok(I5eڃq73~>? $Rǿס?$ς:3&Y8o*w<i{yp|^#.o'}E? 8bW?aűJP w\|MIh~'F"p{bd!H$\cړLj~;ɶ(>x{9:&K'9όO|y.o=Yei{~IOeEZ -oqť7Skabȯo՞uMv~$鞗w :po`>*URLf7e%LQMcHڷҫXIRo=_'wTkݣlҷ ߗ7J7 To`Wno{؇M ,oDc5[; ہO=pr'}SN/^I`vɆ7 y~4xh>|88h)^EHcAO~ kG~ 88x$t&?>|8p4Gbu;_8M~y Cf ;0_~ 1&`| 8 o{^Lc׀}f.2pWa 8p(pp [_{ef=0ORu;AcJu[ . x?A7n8n%wo7AN0\&pC׀{'#g \t>; <8x5g7ځ Ӂ  W]h4G~8ا||.pEƷp@KpD q||/6<|しf-nWYbb> \p$p %&f"{OgCR(^m/-/eÀ_Yf|^ _eLIPwON`? L߀. \ rp4h‧/  l F퓍oOt/F}gA~+p(p#+ o`L6C+hpЌ KS\[e*O^ ` 4hs\ )p;p,T%Ksw6qʬ6`oVc@<8x/YAk 7 < x5&;+pqJ[F<|W =oW ( 1n_2dNF~֢֊Tܖ?RY%U.ϳ_}_͛)Mqx<)/'}w|Y'ګ_#@:oXgwk7c'JE~NoO۫1<9Nm=!u,>pyni[ :=\q:\? Ef4kqB|ۣޮ0Qy}%0ɛgayPit? EW s^%2J /Ֆc: |cyӐdY|3Hn噟{snRx-BT\Z_vBJW]Ypu~sg|fz-V^Ħ@m"9m|ྲdׇ 7U h{E ׌䎁dT aP`mhIqnAOsݓV޼ta#\K՗zz y]Q-/߼;xF=9gbǨ\jmHr3uiGsڪۇ8-#=ܚaIZebض|?\ \譡yc;m·Vtzؾȵ>ǡ ի! XEOmO>LJX~/_ϋl~,(/++Z|C~{=q[!4GME_M +-rDd%;f)9]$7y.M((CnO?-y$2=[Haa^gIAW۩h~I-{eJ=JC/+::&zj=}>*3pkn9C.ʢ'\&a]Üm]z-OCn`"8Di8BcBAgNRO݇ pV;~0oM= oyзپ2Ϥù7UǐSM Q&E\; Ŀ:aQrk\:o}V:䝗#<[7PtEe.qq_mJ1Eߴ )y \dokίk]_jn9U$3I?C$ k=[ ?{f3)lWE^g&Yҳlc%#\.EPtUE?.Nc]F̝/[$%];' XvUQ+`0)3r锿TQϠá}jL>ᷮs潒}W\0٬x._lOz%F>*3Nfm !D.^N4MuzTlސ Z"䧃)x#'$ZVdWPpv7B~Yį޶E󐨯/%op\#M8\V}]n}7#< 1㲁/CAmeW^مwLoW꧌myݞWUxZI O#wلO*_G;m0K!b|;Qۖ~ȕ.~mA'PN/[2khwLIN WJ &m&w\JHyW֬ށqvhY|y4mr)u>rX:E擻LQ:Z1 ϫMV΄h[u_ )_a;>+}dO'hC9YGc*<^$^ѺE0}Uђ6dҘY?8_A&ɄO{(6\}`-fR^nb3H)0+|ϜZ|Ke~dr7VهqM _Jn*-{(yώ{=9.~se w%:V:D߳XOUg؆w8ksG f(s=w"~VXƤekQm+O]9T>5E͖ {S>_ t*.w^<7هϹr,q3+IuҳF-Fn5Hws:q>叿n wR]Nf`=@l-n%btTgk정@+{6p; |wL"kVcĖkJ>kLoN~7/ ZHj\KfJa?BɗWluZ=znYឹG! Aa5*1/er/pR6wpgG"úܞRm_S6͌W7iԝ7@r4dsn>Jwje OJ(1Yğ{g{n|aZ}ҽvgopwwʇ;-w\OY5Ʒ/e=$iv\9pOWx{z)j-[K⋞S>,^[>.~#Z ?%χgPK}yNZf| E@nkJY)+Ӝ#x3:&t#Ss.>屋)$;`A}wԙÉ9T? 'ԕ}}aNngMegy5k$f~s`[fm99Wf-;KyNy/~+Pj71G= ]ǀRdlf<"iWg VtNGw>yww ]:b5&ܳ>)BOzeA=Ļ ?#=V4$Y[tq~WDpsS>Bϵ[DsUĿbA ^Ȟc pϤyKn7/WΩ \1;'h~*L+JX'+^]㟣>%^U׭>#΁Ώ$ reT<<֭qn,W4_7@jdC8;v4}8 2cY͞7_A"E򾼤aWۼr;m'3NƀoذS%_Ho- Ï܆ڦ>oCoRrƼnSe&J.?%à?AI峒 >g Sf2~s V8Gd7{ \Oҷ" ᪮%UeRv8<Ȫ~eiS|g7֪W!M'wx{`xgo<3\|;}p-q>菗/g;Z>ȄܯKu_3 zqǷ?Yw */ 5V];>_olב9_ n  UKrF _;oh_q{nx'׏Tz{-@]+G>Pw6Z)KFb~lh>:ySh_Am M@8paw)9_E#mm &N=-?c4(Km wfN;N _~;~42ًgḽk[{pmtq{=1п=p((|CE S5g{ԭ厺m_mzN(>N Z |FtX#6ë?H<,ܩ@o&/k!)Cp;KEK =ni+qo=g ~uܵ:IrmˆFocA^qr٬tHN~bϔ4?e'܌z/y ?m: p-0?"m_D#+ }vG_'(Y:̄ExaIp|J.IeiLz4_@%~m~z}n%el :sA;CL7\9$][pv[zJ{xҭ\Or=)6G({-QΤrOZ\g.mO64KF~$GCso ܗ.ptD9mpC Ɩ>N_ {ڻ}^]9a ':\ =uN- ʮN!pm t-dgdͲ&z>GU\gV;ສB)м޽Aѿ+%h7]Ü46iGdOuCHr){_=Y7>I( 6{Y]E+@y}bg>$xnyrYieU2qM2Lsnj9B;|ijm"vDY[&'ro~ n/yAgI%[ٖo tVnm:'~w6w>-W:(\bߪr|h/xOgVt<߯ ZYN˚Gonho-D=Ptnzo1enF!q ԥ6(m 4m^&=#Mܭt:z*P: OҞ6%qixCV{.M%@Nƴpxup< {aƨwi{nFwhۃtlNK(t+⑵}7>'灖s758@.g4ChknIg)d||Agd(nV8[z|0 pE"5rYH{\fl =YUao _"?b WJ'?Ƀ5ebV~/}M+Z1{(o9[U&d7 GEO =_*m SrIn9_]vR_%)"_߲Nm,CFtŸe#ge\#>7cL>ێ1KX9W8V́½o5}kWfZZY339X>|k /z|[+[46ܳ/>fc? #)Zңf< Mj>|P߽ ݒkP(SwWßvQ82qB9r=ڪ'hsގ2zכp<6wgBo\??i?J +-z-ӞAmI2c6( `տ3=LsI'}WAw\:s+lcʴ/p7Ւ lcxe ޅp3'[J[Tu?O&O@֗YV}"!~?=NIsgnhf:-/V\ 7SŵxZ/k K,xϿ_fI72E{$pkPtS|H?{[:`CŸGcw~hCn9I㟼r;H.]J݊>$-'cMݬ^7{z_e냟);jV /\{wٓgw-6mr y?c#xiv}Hd:-0I_rWy 御Yyaoi>+qUvr"nwV) Vfxm=CKw ރ=og@8+nNd(Na=ិi40g٦5,{X}nПkw KCoɺ*-05Y,=Mo:kSe|WP1{`XaR^%&x~C9-:HMIG+Cy_GvCo?}}iJ*|TskJ^>.6b=3X?K+Zޕ`\*?Dܧ cGIңLppDR*\Iz'IM,>`F> ȳQ/r!jVǪ3w~dpH~cػYuoje>9fO9f p9Vz\OxO]><&\#ǒ71#;k;v%\^>ݲEx>NJ[m )NQkXzd>2PYm$nN6W_R˘/Yw(`/CW>le^;8O p; pMˀ__=;B_+,ߑsGb?~C@U2\M­WQ]NrV9ɛ =*#| |JÞܗʦ\K~#o$ۄ 7x4w~ הpy &> 7ț7;h2V"Jj?,?)7s'^Lcͽ-Y̻M?Ǚ1pGom?okϾ4Xm9N ooI.ss~O |g?1Y(\W^H@?%m~uzr\WçȘ?Kp דtĮ2[_á/e |_?d%} QRGOSoDnUG{unC J07Rɫ|rUs(HFL䧮onn wf-w?o OPqJrE@T YcUও7{\5~.on$hދ4o-R.>|Ei<< I=3䅫' bn0_x+ R|A󙵓Sђ9(~)84UGe<oQ^zSș௷&X;p 79_"YmY'bJnK}tOW+#NI21p{6m~xϞD:W^9o_~p׳.akN3HNe]+Ǭx%? Kc,Z!k=vOwvO VBum|m[c5wLm(Jދek(8y?H\Z]%JGK7&Ếv)=HߍY=wu{N4/|=.F=?h, )6R#khY7E ͵̛(L'h>/\_~Po>g+VzMcA/%?z^J%\})[0n8pJzQʹ& \m/g!L?)I&|jaRҶH<~+R6_y h^9XmcWeX~cLWx:cOt.Jo=R IW~י}]#낇OB-bYTRh}N"S6;Z]N}εS{ŀwIo$(dR[~6/Ҽd2<"p;& ubMJ p\q-Ҡ?qE |3VvVAN#/lH D \I? p.fwڂ-^AYHŬ\Y+,WyBWM#NJ+}AbEv[){_M~O2~pW0VJjR:`4mo _`Z@dRw 5A[-}#^;}.ۊv mG2z_u:*wP6V{i h֯[N1lkֹĖr'; v:n/ـL6DdG,w[/wGe [} 7S^C 93pΤ=SW>ʷ|;cQP%w?;) ~*=T 7@C?AXD_M4{z&p=[c[pC p/ q;{ }'*2 1r#M~}(>K>>h?񊞂@'N` 8µUeqزCHޥ\R.^WgOݷPßnݒ4$~i?XQsX6vnP3R 7~%gB$ƛo= Ei)Z}!i/#-񛩓P&`$3;ɜE- ɾ۲r~ggתgF-ݲ+}opz - a Q$&nʏosӲI"{r8s2v\#EKt>>_~Vp?`V6Seu#V=)377m /[Y#euܱؗg]Q[ wK NDN~W=wVUOK)i\܀ocV{3-"Mg즵pd ~uGn'GՑݾz/((.G]@/oV$"IYV< ]@K+MO|XEn2^JQ"X^ C|y }NQۣ9c2&O7`ωme EjAOU7??jOF("O\=LQ];f"yۅppUy>"s?؟:Ý4|LŸ\?WpAqVy5rBrlь\<{F=y sOlx֗sȲ5s:˂ @V A1]ag[ie߫^A}L\Yoo2.;lG3>-/;@Hnݻ-d_7UT~Eٖ_[ݬ#?c;ܧ \uU讍Ew3k5sfdlhj&ǀϫ_4*!N ߲mKQc|A?x{*3 _tE#1}Tm6z, V|jUeRgJY ϹJ \Y<=p?UǹGH1 2(Z딻ܾsܥf1ƴG5nŽ9S׳0|Ok[/f .f)n rwo;s;\CR$|6S+.r,7icy9> ?ioa-r-?é~$ԙ7 ք[ﷃ=(iߞr4rbS]o* t:b|[߼oqo;j'61f`جgGb׍$h^/ _>Eѿ*z*^l|Z %gTWR񊞯;M..{E>dB'½VG]s[L"?Pz&YujC rSHnL\cݹ}'!f?Ml'^+a5Qp$?|?%/4stid#?L3:y.TqPeVozaL3.u4k+3O֍Ҕs9 ?i%u&p\wgH Ӹ6|(5} djn+9ҷ~wNmsu umcw%t墢;MR&=-?'2;3}"7MY^xoYoq65v[{t,3WN(,h>'z;5WW$?+.hMU-ò"O:)٠+GNG_CzvW{)ݲ/4߹ e\қ'׷ >AIi벗G)TN oSOXW-&I\V[(mVn9@?M]\2iĶRşXi W{>U+>[Z'R*y uhcm[i >2o?l TK|;mx`S)YWѽ6PM m{J_(:q 8w2._ʌsBY.[-3}>Iڦ&AY(Ϸ)< ~9Qiy­_Ry˞2-9uIܲ,\ :%/g1}bઓAKFП\a#7{<,=pu":%em#\tcP'٣"uk_~:dޡh_95#\'3w-$kAu>q 4F*=Cy+pm>NkocDJmp큳߻15|Ӆ)I!Q-JwVeJ:j4w  wS$S&oJ;El7] 6߇@r׀4?@I#e[ A*~^TtD_a*=Tz?OKYgְ_3ȍX8C*=yό'ߩg}:`|H?IRxZß<>~J|?3IOUgZ,S%p\$vf]VNg[~8}QOX4[ǎr.,p W/kM_l}K7,ۃCE^fy\w;V]w4GM{-c"]/wZ79cAn詊cA_܉X!ku6-M(җsfq}Mqgwd0KZL}π?s 4y+rn}jk?[$^Ÿ-wzwrJ̃C3u/wqa۲=*wv,ߢj]#3oH{F~asۺhæd {1TC2&Re)cͼ 5j9f-ZC%ɏtq'$Yޟ3^ 2oApo-\Y欎p2\qx=^se.X4I\ B-}nx#pcYƎ(LDzG Q!^8]>)d8KMS,cDԃcI_;꺶93X Huj=Ճ=uMSc52;rU8SKlFw]'ڸTz5߶?7eQ6\,>[89t^){pUmPnQf+nE_V! KbRgsu=̚K^53oy.*;EA #_Vp҇݋oWdڶgy/+Fr{@9A?R#f]ǍO+IQ{d[$&J^w2{'#{32 ϔ,\L cE Eɡ?NzZ.~cS3pGҎSȹ 7gMMS9'{S>qy{WB<m 9s%kÝ)yHf&1yZ`\j'frN?\ ^9TYkxkrw.*GHoP'էpR2-Tq7#İ捋s9A9zd͇5}^| !7UY9 4?޼V(LNH\2wHlկ-M9/r\ﺐg |1w-ݵ4 /:7G+mnՏ|@n-sEi%AyJ!#iz>x7;[%_kk+ȳ#ym`/Yd݃mvYiKfVb_lׄ n/sy<Yg=$|'gT6`c RZ[B<;F?6x⌓;b'?m5-vXiĿyz՗:j.2k@3EG/ eUc c '*]ߤ-秶Ƅfߎn;S1 ݥq6JAC;I y,Q0b\|NǰWAz*"=࿰fk}tbkKlk{69kA󻄅 yɯ֛o~ڞZ|$~8'*Z|O+m/m%䢨88 |C}̝~7gǰ,`PaÏ|36J~Y|3/z6ߌGOOMT r T~84tG$h |"ca.nv= ^4n*Z/gu T[eQ,WD.?@8;|z˯D]_4'Z0^`?lO-'}ϧic;q}YP|¯/]wEf7 u ߋ*sRw3V۸w?S6P|^\kA5-}El9- ڠC= \OyG6 !GT𫰯(Z=ߘjHY.V]H J0S׋"r5=+6{|Aʋg΄F}cZakxg_]|6o!߈ZV]))<\C?8pG( W{h_2#侱A|%_@*4۶7[U2:/}Dy&=9q෱ʝY*Y;߁Kx 9gwV}&wSU9b [i_ ~7b-4o |_ĺ&/;7[`m{aO[ _?J|/=pCIʵ?7$+촒'v O*}4gJ#ܽOr拉WI;YLՕ[럂 U ߥݱK2:ՌW=3]}h]>";  xH/Cf4 ~Kj/q_ Yڐ[pٌAn+',<2iO_9^ȟ?蘿?|5 };YhzEg)g*wp Ln5x'ݱ 퐕&7B6Vi :ʯ=y ]ϕRr_a~IJc #-߂Tu9?g?e1Bf݃mٷrǴ7{c=NN?m r}DXg-Yی-0ܵ< ZڍW&]ѳyGeC7L M~coAup{NP#[ycͅ\T[j+@>twe7 cQ!_6kwE:C`~IcMB*|\oogu6q\O@78UVb3̜֙;m@O_8o$ϙAKO, pE5o!tCYOOGuS Bغatr'd=TXmm\jy`k_t|qNpAΩo#uTr;%<\Dx+n0ˤ`s翢g"|n:Ӄ7q^DRAEW8N:87~|#I}߁坁eםN5̥SzzyTe?g2>2ryx=ඃpx+r|/-ܹ3U|VG34ӹ|Q+n`^q?o~JF37_ƴ (L \)eXJCw WOmkdՉX) Fa*ʝCiȾm"YɻO=8 JjHl彍!W*o£:zQ{DvGn93>jVeB>Q<^9e o9󡷦esy}< ,wEfn ڪ}dL YbS.8|0E?;^Ev%1yjʍ pVvMdp@OXn}C"'wI9+;g~c.lo~#%Z$u{|U5-=xU6K&p-t>y<6&/`#7t{ȵgP֖=a^KMBׯ&j.0>)Nc:y^hCG]EN~OAHd- \w cw!|\{6;$r\p'6IU1J}r+ՏqfgC@OƁ\o>S}:`AԶd뭽>)|= rRA5_ -q}nݩqZʌ=b}!!7̒Ld;5S),ΌRx~s­ArcUo<&1V,wpO'c귐KrQmIMi7Y&z&z+ȱpEj4? u;:p5A'HnS,Wɤ[sY[Ϭ\!^n9;O~P2{YgL{9 [ͭ+r4Tw^S.Ȥn2?nՆ6~'Cڌ IsfNGϘ\r!Ū"f_3t~w<)i;vP7mAGy}p>*IFL^o>^HTtxuΨn辊U<s_}[k4_=EFZqI_^ʖ,'<˕痝O sDVYԡ /b+ċ A<.̙M(kwLg# rwHnP4>E޶WRo77,heתk25wտ˸7yqSiB x^x*Qߚ:;7 [@N a֌8pՀ@gf4V EǭkJ)(Z!)]Üui\"v.z4gzmeUB<'KrHajoU2oB I_]:,{d摹Q́keܥoVcsiu{P|{bS Fـ";O?r%7K\8phOm"? sYʬ˴ )JCLz3XtʋEz?|&lM9x7pxC^5B?u3Rlbq# ZEhnCFcwKtl֯W@ِDsg5c@K oѻq oC[7RSxJn+q~imn5VЄD@ՆI^_ܺWҹ*88խ9M_djZz@z~e3NO5`_;@˷+~8{=~~W]0%wRxr Io)Yqp8 1acsӓwڤ퐻6k+Y;NLS¾^r}؏'I-xl|ύ7k9f8Iqk!u~ۤ%/m?~Piؤ~8ɏu7Â")iNP{(~edC' xiF p}ಥtqՁk限;ҨX|x Gz7CZ~^~3#co pwFOȖߑ̳F&3>]duRȽJKjWlM|QRbӉI*^SD| FoUV#]MnOpI }<ȪHKn~'uYOG)O$߽=ʗOw*=c>f}qh'orˬuण9w{C_A^_Fzdd>?$3^A1o9rg~&묲緞#Xo6n91ژ> ~fGo^+>uZmUԠPO*o"nJ;% 5V}9 7e~e .h ^o!QJg$Q[jx>#ԌMy|/ڹ{{^QvzB[bRJ@Aׅ5׶%wY;L<'\Y3 V}:ԓNem8䎩lp,?m9_;ZR]pMQeԋ T g~|a֘O@J=+d'pdLCoH@99!hIhE..s*ZwFw'{t뷍ggk 9}6U- rFqs?63)`(?O٪Oߌ/]f'={BԦ~h/ߩˍx[=~e/Ƌ+{]HQS9Ixs߃$ tXj=b>q~~fp{ᮤ杣>B Tp=jodžxiOmYM!NQs6a?(Οk9j )scw=3fQ9c)>> =&+U#9?7qGM[ sN (9F."Ok},S3-!],}[ߟ~; ?o:MEc/ZU$O?Rؿ)G)t#67x3 >f 苣c~<:C7%PzxX3Om~"k-rߑB҆:)'S񗤺'Y:KmiSj- q8h5b~zRPD9n} \Z˯ kb鉟@ٛxן@1dE32CWF9Sӕ>+!^̓=u*n (Tl ~)~ʾ-|ʏAu숩#M~&^SトgO̺f^c~ WHtcsN% D2]mxAf2Ŕ&Hђ/BݞBGcKOV*/ߦ>90؄(jo,(}[cx˴#J@ς|9eGKf k Wpz8M<.]0ȜJsO]_x~U5 'n-ϥkʉY͓eAPO8i{`'ܷ&ҟ*حKoM!0Ä(Biďv>5zyFo;*'·Kf_O\'eW~+3v;5b)d޺ƕPn>㳚R#+>7z|Nnא`|1G- -7.pٜ#'x \.\[3|Z#yFM;+9;B~U.^nſ'>hY d'[Oa[^q}q^h'w垃cV2߫˄ckʕ,&X}^6c˶ GI2σscoNУ))z%>,wnie.ph#mϷlmpϬQ;ts#>~E{A~5!a]y]8̙_˔9wz_2W0IH`[HN" [e+&]\qN*XecFxӵs97C>C۸~{6nzJXsUHVzl2\8䣐7چVEG'=*4Z>ZY is7[Muufkotwj?moب 7zu-i \b k+zm[6˅oRC>F2؏ڹc9৅^yC,~O+rc:k K ~;˯J];7ڣ{]: .40{3FJЕz˕nE{BKs7/Ϛ46_@V"W썈\.߇ۙ.nTCpH]=4 s~Gm/)(n0f(z+h>󛤕9=}H^J+H]1%\#p#k6#_@~A<uC o1͒KLۥK Ani Iae#G<[uUVN[}Ip7`Y <äqG tuC݁Wu}N*Gys͹Ȑw>M02ɩqҀΚ+3/XZmS;"p߭69wp@W-#gwj> ӀF2{DlG9|G/T|޷e~?y?,/C=ݸwJ؞q8o@l \K?ػEc;hC=qi[}R`D4{qUt^n\[+̷HC@54)&U6X ~&ox+j!ĿoyGELnoʆbrn*ZtLejGM/J}L?[7^@>?Dqkvg/l>Pkh)+YCֹjZ5J 5}ϵ}'= |VM)7c_x~k葿8N !=cb T y!Wߒb+Wͪ ɮS}o ~u\o~M+CCOܧO/\Sf)h/s؊NۏeMڎ&78{si;}w_n 74ȳ7prWcyۢcjo+Uy%p7@3Cwtzy.p]p݈{k.2MC-Ewцb]כX2b^(d;g12n|~݈‹`Er9TyP'9K73gYW P>['v2ʞᲩ (5>}F՞{iaeL=zS`ҳzƒS:&g߁?ʯT1bMV-}[a ubuon 3}Ln ;^%g_@?ElX?^o_dSwIo We[l-#~ ڢYP]{J j­Q:aQ=w x,u۫!uȳ:6ZAFn3ኂ׵e奆q&Hn~Xz9=.MZ4XB۫g+d .0,/= !+]<g? VM6J8I;N\C\6$zꄜ6mw36nL 1s ixo;3!wrr&Q L+9N$w'ow?2ψG-u՜ _l4;RpYAX8%Q(k&pX{gI{5k-mca8[[E;82?;5t.>ȺStG}l_<Ĥ;h?pVŦ]nx۴ Mo1G~C7dcp̌.pmn}.+MB2}pxYwgH/OНm A.f| +?|qt=j;AkG[p\6o pmK@;| ~tq4&R:+؈HmS#"u>hLT4{CA?+nDu~ \FK'K8{cFiL{~wbًQ=$W @$ޞV>$^y+[~PeuE-ڤ}??N;;޲?^?c7 .{-3%_jlQ\[m}x8t~ An$o8?osGkSxPN?Ց])Q@n.vᷣwO Ws I/9g;z#HY=A$w|o)h^3X}] Kc?Iz3k?뾉+|\8_Dܾ)%nݗy߹ pK}ǸK͙;H̀_cqn6r۷c9N:8gn ᎌs4IV27Jo>F<-dƻ6)3Wݶ%~Kp'- ^o`?LCngq\up|2 4<6tJl:sAdg2&!,ծ:-D!|!KVkB24 17W1-g=J=J/\O[}OL۽e3V_h=y Z|nz@P>m7bCl. U%*=ـA@?;O|-ɔkB[G=^ÿn p ܏̉nXcwN9ɥ($.QҳMAj` p"p(}SѺ,D^گK feUΝLth,}:[UiL{GO }ʋ bszY߄x=x+%'\Z] &E/mAe;to?\WπKuˇ:+}H(t'YIS" rD9pqH;+z0hyYd =P-g>?jYKJrW9ySE벓|GjohĕԄ+5ۦSOϡgӺ=f[̶,nP~6UuE~^ p, .gϣxvPM_I(6py-?19߼7ٛ.m; U% xP+ n( hyAxq f}\󁘼 \)+}Ӆ: e%# W߅( \yΒ$yqu/h>u*Ȝ}俏R6J5O.=8FPcBb~ Չiwւ_m%mۢ3tïś"Iӽuugp(_42n`:?&Vwfkw}٠ jpuseVVx&3Ƅ>)#[[yKci~[4[(\ \{KmJW̮yLa=l2g%8cJ?A΅]Cz@L޲N9̈c ϞrR~oNlmZi\xAw· 4}NGG>ϕsΛߌm=CG+<ɗ̈́Ӂb7_aJC[Cw[[ 7jN/68(ߚ-ٌYm! n\]M+S&D2RWT png+/Q_3gknϢOʠ#p3 :/x)>3?~3ȼK#Vq^fq0[4{(y]Lح/ Yuy޽Ym_}-}]H ~)ώX9/2#+[Ψ$̃„#9) B~viϪfgc4,xLmRmPsT[xʛvo7YЯ yΎ~ks2agAnY:tXz;,5ne_kq?=ǻ j0%u a;'<ԏnQ+w̲%+){O~lc^o{?ѿU?3I|[e}̹]2~31Z5pSNCedpd_"5kO4Mz<gm{~˟$m}Oo̧4 =OF!;ۜ_u8yOq6oO={ Ŝ{ {cRGS*c7潿 6#.Fwzt~0'7qsյS.#SC4'NH[}ێ| pQu2 ~4/7 ~%|w'eɸ$lu頬e/ddzB~SMOÿ:~"Ok$9g$Ӗm?exF\ +~_?c?  \j+>ea*)?Oڄ KOrcDCCr]GaU2!PՎ|]yjZ@OgA!)o?e/{RfSٝ)U&2ߧϫ|m:|OgΓcOƏM6S^i7s |ivUyeTbįm]xz_䆃:;_;_v: q/kQ8H&[zgN~E_S]KQgΊZUo8kY81S}X|.#?~=p  ,;\5iU]E!nc|U9]mA8K(kS3ߌ]C@7]a*"<ߝ.D[*zg(zl7yE1y*N법fCDn#'w֎+WaҖ=AeCޅ.Ifm]E!ׇj>0]|ǸuNRr?H~p8,XuDFzK֩ё?HUORRMgZr%$MiESqJCv;fS4Y:f})>HDPLa2#͊:c3-8 "!oiO 6uIOfE.=+i ?t`'&1W/z$=, m[ ägʲc}:Ok UvEy3Uu^'7N![8g4p![h'Nϗ_P{qbz%[S{r!bmiIx|T+oG]#9+ .68>WC7Q*$#ri-}䁞2tr3%+Ƕ`[{A>mo7m?6o>Rxd*]Zחxr#od *!Nf | Uv5%Ԟrp9*K(\S[> O&V.QDە#B. 3_A3@2{[⚹i̡JVɿ>'ᣯ6tʕe`[G|Ṋ> b?KK 6x3auT vdX99 „ڝ+-oO ڽ$"?#b{I=o1rqߠsb[-m^@z2Z9 OU#Yg7 W%ku(_q/ B:kۮ'\--Z`5ǖ1mBX8+mWpxB.z$T=.'YAi|;?Ew :EkY||7΄?znw=\7yڊ[I/Ý  'g?SJ>?jk𥮔?*Zd8}Ʈ R+t_|FkETFp+jt-ґہnQp;i|UES01- Iᴞ+xpq8K |~]Y`30M7S7yNOmB(<߃=[=kv$gׄl\@v{EO]{ޝճ46ۥ苊Wy߄E?fl/PeQ}yB&;C>vPbq]452\YӬuƯDs}hy/ ({ss) ,y^gǡM_eӚnJ\an WeoB-[~ '9J?7I&XcХiDPfar|wCnU^,;TaE7|`cD_澈I>˯tg6o=z M=-Oهi8nQG'VXCaທ RIJS:'rg9(X~'Xn=Ap\D٣෹*Ns0_&#Śk _%/ ͪ?lOɵCM~omdS? BʿIw}2GCMMh١22*w2 ω|; Z=DJ?}ț ?t(~%wϧhUrnw~vo}>9o~p;qn}܅E m͚s}\|e7oWPfQ_N6%>!mq˨hV`-o6M]'ġৰPΟ' *ߑģp2nk-&Nvu: u^{=I{^3ԡ!S]$̽_pud %Tr8Ċd޵xWRKkT'}N !`~fhgJ zxtFtܹ5{Y嫚|TB7W!|%;J#r nU_Zj*k1~.ہM*[~˕@$+qD帼%F*ݺ^ZyKo;P $?qRvڦ2z>%soA[T|}JGS{]'%Y"s]@o~܁\[?[qv7Ͼ8ʜa-v@ :.oq#{vߍde?W{l]KV17nS6l傦9 4-s'5-c?+bސaQ> /WiC.@C}oifl~˸peOgTx]܇n8Lh§a˓:i[xm(vWo\ů}Ԅ$e/ۨ;#7,J~p?TȧNn-]IT8:}7[/ }ewhNØqWu|ߊ~!!`<IA>ԊOe>_U~u:cI`c2rK-?+'Ore^q~o=QʍVyX/2.`|͝:[o=b )56*u f,$66eSE\'U\~K~Y#k'?pI ^'|2?x)7y3)4/|T$7b; Z8h.Oe7"K%?e'# '<^.g|kd>x,9#pI>nロu3}5JV{˩p8~S˭pZ0x|.n~{W*iG_HW®ö0 f6 WĪIMmdgо bVz3wv_⋾3$]&U.3[s}M*|yɲ3?-`GDŽ9dzF\ J|f/sTuBيTǹߣ2ty!{Û/y~=5Yzd^uo@ֲ|2O'7%ޤ>h7krN]?վxe/r:(| |KG;yXIyo$89#7 |,|kG7EiG ZhI[U VPb*=z/|Gg}W&3-SioWUCxI>uWe; tw ~%)W{yJa&o!K×~;b~W券?.,&>']r:)rMHG"ύn0*Z_7盵SL a?3mNQ-J'<ϷrDowfQ[~ߦU|/A󙇘[3iJnLyj_tLUoRx)co1Uca> ps-Mpq|e;Q}Adx*+]_r%عJ21ܯ~pҦ nնJd,f/~UWft >+}[IrgAa#_௱)bs_Fiu$9D6)7~6 bJ_%]d8w먎ֳ]$QҶ {6CTic%?on[dᔾ' =V?;G_򄛯'qan ?uq;Y)=wZWwV|29-3?= >iNzv{/sL]ƲEJ'0W#2hYK[ei ;>Sv.().}#:{M9gou|pNymZC#zr:>~kQYȽ3d%ziKħ}w 9޺_ ׳tKfgkC ;Hђ7{G,"tmmo% 8o[($wjyjd}oQ*7^~ _܇oU?{x$[k慠۞݈>Qa.+{cO߾d:p)3?hg &9MPڈ9yl!vg'\n:q}Ϻm̳֪Ohಫ3D:g9?h0-H$W4U:s6ssNWA λgTt!мP 43>@<{yUi:z~ rV]A<>1/>>Å-~e¶L7_ps傷8THG.6E1q)\=ee&|wH˚5"EJfEZ5: HӴI/`,rV# ܏Il?F{d>S2uw덤mרi~;#.:Iu!>m{y۟źd9'%/{m;8_r}r5P38El9J#uJhp?Z+^'^SŬ3䆐ME| p\ֲgQָH:,r 7r|#wW1g[DOSzƪ!=rS+C~ Iy4njt>eܗcO&}R|ghK]Qϖnӭ,/p-p3 a,M/?GũTŗܷ/ ~+nd]'-p|Jj;e,PO~2 ٫ZJ?cK93pԧ{d_[^;)C6 f2{T2$wVeW*oK *_uQjD?LxZꗄ]Vy jik1!̫n9c.A~J3>Ay[Ѿʠ`'#MM7§?L!'9G:,)qU)x?pgxsn y!_$orv@kRU,<Axtļrv7%pH>uw/eDKIln;'7יӷݵF9)ܶa O|$ Ͻ?:2_h]v3?c &_Ϗp$-,בBn+Kxe}u1yݗ .UöSoDoqxLvyBҺSr60ʀhBX|k0{UN q&gR3G=Ỽ}DȝE[S3)|'}$GUz)qn|9M:b(h#WڬuzE|7fPzp#?/\bɻxrGf/pI~.6óKNrw@XF"ayTVM&rzIK[F.ʛ~'PIKkml'\[n*a۹G^s<[P{kT u'r|ΎCs\6zǶCc!ӏ[U.l&65جSv= t?bߣZ?\/ZS0ɤ}oIyhus/FrxQڕ<P*O>|>[ Rp==o%wewHsH)tl:b7]X;, z(ῆ8Iv1pp%}?[%y+9~XmP_u,pJ W/5?@c6V_%P𙁯{Yj׷!c_Fo|۝twQ ''u^[wʛ,Z_Ź&<| վɸ~gzM4TA }ʶ=1pIK[;FKHY&UcNw P_:wPef_ٱA=˼S´d5"IvSKl%}] >֞,݄/mz79xߜ?]F0{Ŀ߃\OWG?uZDghYu}PAnU"'h0UqE?]m3tuIt)t=[%>N.T&M כ];&ȿoAn*)~kw -oG}[']X ,A{d]4#]7~qx,=< IfdܽFDm_'>xם]UW14wLV[;&}LгS;ޭZoNoTy7ڷlef8 _Ұ bgI;uM0H'%628wZc_o\sV0s6UxH}W+ڀ-\.TwˀC uGX708C;rGvܕ:ϟA1tD/7')%/{oOݦ0-q@\'?⺀o ち.H]x  ]}Haf\u!pT#_ۡ.5p7T9pya&ݬq=y`D(7i3֜LIe~)-!>~);]Mw-y^R,֫} V=6p?AdO4+{yΛ4 yhO<$6|J;*OC%B-|t+%ڒ;O?mOm*Ce៾e7'~n;#/xI߬nulKʰ0;?n7D)xA.@'$ENڊuQ^C={ o>g,2xlٗO1M) HD.hOB!Na .#I$*/y澋}h7%+ߏH{} $lNğK`M(UiIcw<F5>Z9k„wo8f *=, uܶQOnM,8S Y Ag|p5j,ݲ0t&s?suqQ^VQTao#]B'F>eeJ~ q\*!Q<ߖ V^x!>Rw{ios}1i*poU\AU *zd%GضQzBO?qWq|,= ]33˫|Wh=K|nCy=WF+ }O}Uއ}yGmvA8o8詨FOPmBf<<^*JF+Fa37y-uM'Ky?vlTSZ'6qr'DzX^| HD4؃ӆ璾uZ}O3ruTmܭ~= _iM%w䆃t|LZqaKV`?C~#$?~:?0G!ć3} !7[́\>yɶJn!Ze1POf7eakj9|>}KU;>pU|+,`\F ۏ2qjȯR}w 8Fk(~Ag#xj=gt>pG1+ ~0|'*ĉGb ꯊ쉧B-#Gu^r;;{␲ |=p:]#rՐ[n֥d?bɻ^.rdI+yyCALW =)3dy#k#ײ'?J#MK<sI%V?Dt~$NJ1kSX6rxրzJdOX^b4%ЗFKkvb'O廬v3W>ɿ~R]5&+z~|FT\\ӜgVϲ'!}ɟ|6%݇<׾C>h}D|[ŗDzݽuq6c]~I/%d?+WhOľuqy gVz(=E}SboʓI?#PE 6L?Wi+m居Ka/r)>x>o ]w)%&fn'&1~Ua05(q{o"4`Ӫϲ?-kDaI{=ߐZ*>ov%Nk9b"|Kk}hޟ n#}旿>R\'ϽqxKn5%Zo4wzi~r[}?G$<wV$/X`\(pjf5,{0墾C]|@bBs6 ?{% .t"|&EߍU-T1=n! vνt5z>C v~Jm}T}=!xᐟgL. 9_`?_H]|@WW&ESt@6_Ue.9+lZkt- t}e/=Nwr|W8mT~՟erߑ1K+g뀿[berBc#;L_6gӜ$,C(<Äbȼ Y~gV{C6cg C/Ve=-tnEt}#uϮ/U! }~}jd~~b~02"o+ݕ&Y"/q[;-ѕc 9-_Oc_BLQm:7яT׫=2>A߿쓊>֥;34 W~N,mfz 8"6)?+ρϩǃs꿂sbW[Μ?^r>C]n!ݒ;/Ec|PyB[/$?as*ɸ^"\ɖ>>넻nN_IHвwO3 LIO}"Mګ:> [[D6㼻'|jeqy.c_ݤѪ_G2riI.}!3#9ǜ|—Q.@&"o~j]wouYFu MgSs퍸f+2 !q2mr[G-R: /gWqя+pr.?+p(b48.~)U)!k !_Fɗʟ" _ao}mw! |%'}wE$}S!_UWkW_]kHԵSƆ8)ߍƅxpu|C|szxf(RZxWQW Q\)ۯ$V3vj5StEOVZEWE-w 6gB\֒6)#\vEVtEwW0EOSM>v6%{j|wGwE{?7p5@Ga\6_P )li/oEӔt?0~kb럁|hܴyM4~Y ŏeNSSSc(wgҽbmR񙬋0z[*tRŻO?_k/Y޺76FqUM*~|_ZC~f!ߡ;}'2{ߙ*?#2Y^+\ŸJzR{;탞ߵ Vil?cL7"1ݹcܱeLw'fĹ6~sQ][ gxV@4mU${s?^Or(SPGoO[;%Y.]E>{jy"ˏ9{~?BtX x~v8Yq VoZ72xm%o%`w4Q\ѵ@Y/[ <䵶1}.Drw%~-u?2tX3Jiŧ-GV~zN.z@aY㺶v`{p3*\d:(z]ݺEe/.!ڭd*s鼊;>~Je*=cDg# w:Lȧ!Ϊ4>?>_Kk{K1eq^c{4v< |f+ +h}el]lln]ii~r>sgW,g6 Foq& sTZ@@W3%pesa ܱY[Kk !_[ Mw)ZʆJ-eHr7o}I.Zy,{rnC\ rLqhI'χ%0u=,_AuzvoN1Պ>{֫ױ0w__1%6=᭡曑z"ly!A>~mYz[m~SLDC";G6)Ji8pUR>SkI_ւ71/%rFc(nc'ҁ߁ ZމalZ WΪ6^} vVShڔ9B5w O2=| ]#|~q.(ioq* G =}-gŪ tb-CnU^S_wQ I"nq;Sܣ8rDžVr7# #^w77ߔhnE7EK9Z'2 8ҳ4wtͮemAt-bI'vR#jvϐ@vg*zչ'gӖWp(; jǦvwV3R[҆ߵyY~6/Qhy1Y}1ܤ:+r;wΈϙ#w~W_eMvf&KfkT] 1IWfȭ#R=@o|ܟk_<MV: qb?<`>98\ 6hGF?~ev2g|'/Jr#JwZ'paOrEA{k@\Pt_{θϨAGh;n\t}pbn?i?b7ߪ]&&߭9nd ٲck3>lMC}45(%t:M^\ [/>T}CYewT?1hzV2QSy.&4mKzPY;vqtr8>Ҧp)'5}C93r Hnh9%-%Ƹ\wI{Gezw~.UjwΙ t'#*;Om4Nl tV7ǧyq}2,J/.(Z!2&Y 5.H3JsXWR?] J {WܪO(m7 8iz+}0Q0~-r++l#d!Vjo.&G9wF}|cr}-9Z>)!Id4V>QoQ:0 p?ZzЌw~ kUn7)?57 =ne WWm`=&IzFY6E~]7p'@L]fBtrjQ=qWnqj6+#e2)Vޖ t&Ӥgw3[s8z3ts/px7 ~z[m$ݼsn[G@~c!g~1"ݼHd/!ٜ߬6?>5QhmL^8w,p2Yfp_C'FI2>g(g!*/;o2(pʽDY նq:Pȸw:4{*!*gA>7GFs!hKN7[*ۀ;hN/r࿥{< ؊g8ځwqY 6ۦq w܊K|fNdVo7 wlQ3\'S# E|H 3BcYhE%.&/YmӵJʋ+ 5ds}K ep:y  涥y|'H6>´Yo=Y6cH!CX{fe.eQfw&LH ,~?iBlRhV:?`m۰ &$N&9M1!q$簤M}~li52WS|pI~|Nw5So}6.Be'r:Z_[~y37·p"g9 (R6㱝ƂߚKAݠ\)|쾢ist/VS߅O6в^$3=[ǖ./ϻb!My]?^c}glNA󽴐|?Ȫ֯q'n0ҀnN:)Z4r ?'up# t=K7+rcQ?#ѹ^w&oiҊwtSGz {rCn0!?Mٲ*39o`{4!?wmJ@L_Lk|&9ZC)e _UYv Kg)xҮk `{kU!j %_%6,3Džz=r6ۖ!7Y|!9Ѕ`nŲB6}/aY:>7FAe+攮/;'ۧҧ[~!+w N!qW8q*迦PVo{| (dۥ3Z,+o> EQ|ȟ>%vy]TwU}}YN:o{i迬_9.a|C)u*S98Ήbnոe@13hԧn&G- ?,{ONwV־b E.Zm4Iy $ܽB†xm֩>%ϩh_M^p] ?"7oDغCɏR\|ghgt4ߍKa%^7M\ۨvyꌨr=zOCuA~3z@}Eܸq7 _ﲕ>Oyo73c4xikN *jp1u<߳yY&Yu<&lFEWtuE`?u&HJMXMB.AnfڱyYx|>r~Im8O .f&pɔm~̧|Jϡ0s.(?3!cлh|V 7ASsJG938[<ğJg&~bR~󧭚\6s_)Ag@VlJ!;=׃}[;u~.CS'dP}F`πϧ:ca/o)mopqIG<*{%Ɵ_ԪCrO1䊓\?D~)y__ڇ=1j E>>F--yoK$!~߻ _t-mJm7q],O!y~S7{@_ugz ؤ9^#t^ck]ס8?34"-h&DiMyUKEFV_uV`(*yqFBUUF,7%u>۠#m%]\qWL kw0!fl[)2 :v}%R|@564鷮U/fgjcy{ %*.=iwo9o qoDxwF7hZ6L#hyzk Z|nF7P޿Rc~"hT:_?\u?7~*ih+2 n'o0=%u,[&Yp׈Nn mk^3.ϖ}$~^K+O'%h~oE*mp}WߗL\) 7OHuun!pʫ-~_D }fΛẇ/p} Kw6L= 3'B~UrTN'@n*[=~e_Ls9̜)aKu\x Aw&H%brNHF6 EO/bEoI]*)L䲮?Tt^E7^ȪC$YgGe>dnn亹2[[;Izσg-W Rmx`.U_dyv| /ZN9%o史>*]!܄rny)amݟo9:6o{ i֖_t0g/ɾ.lƟy~=K(Ocgt ԯߵ-'\'м24L{+AzoE_ܟqN_޵}`63jvS=2?^,av$ _[۫)z7)*:K'}򗳢[%@Ys+kgI}gkrF^߷51i3IFO>/c@xπJKPQW+\ IGrקܲ)e[]]IE\zөrTCn8x]Zk?!vʛo=YodZŕPķvG20cw?t${4c_m9AV)o"eH&R% _2T6Ĺhn㍣Ϧc}ϩ𹬲x?(O!7/x| GZ f\ĬsYJn 7̑{ߗdsyNw _5[xϭ22NMewI!WF٫,?w`oߘ*|1y4 ^;_*>{W GkZ5Ur|#cz4O?}o-yY|'G@%-gC-ZǾ7yʋmQjn~o9||s\^F~]IޟU2sUkH?9*o|\="8lEcKۯ3{-ۛ{WiUDoJH4H(]%! R]Ң"(,!"" s9ξ/+~?y3u|O3X6M ŝmmagS&p?ؓ?}a'G8bcp#yܝ'vp4nO42㸍:X%G_Ɵ ,"2aG{e_#Mvx}^T}T5H^|ޱ6"* N×vywRycBi_2;a╾ݷ,u. k^稲 ߟ݂!檡#lʇagc\P:/ٗm^>#~: k'7?rsgI|c30; fLՒ~k i}:/ou->#ې?(9T^<^3NvCx=!򮙿C4J.+fSfn.2idӱ%Ls:tq6Ur([)7RrF~Y&7 .C'cÍ>k3)(U[aI̭=d}PàM,<%/B__v=_h_Ka)gLM=y{i<_ ./ 0{Qa?׿]/kU(Ny753};5("C[y1+{*ߊGAw*5w{`/eo(qf8,2 ~3|~|{ Ryތ0GQ]5\dh>ϲ$_u7}}0%Jn-n}a ~0|~Ot?S|+Ams7e~ߥM {wgO!p} ~fߟU~c? \^½,4[HӦ6"gD~{L6EBap>#E#ߍ4)ԱqgDmRį/ưÌe/K5ԯ5@[|XWx!xkBߡ*wެpskaLsǬWU^({ [_N`W65R{nRZi~o ==OsϨ6[mvcMKJ}?GJee>9r~Ӵ ׈ҟZ|[@W}|=g]`S|n֡oAJֶommE9Kr[9su.ǽ 6 چ(7%ڇIwk;YwCEHG_wZb);qbZQl;S%]Vby,d~ ]~7w|i#^?S3C *Om(aFE}48 `^?cZZyGXܾ[ߋ0+pޟ! zX+0Jн]G|'oWgy7yƢLzB?z>q~G?M'iPqe?N7e$q =}כ忭g}~LNy*}b߶mg& [n[]"na_b࿧KSh+޾i+ފ{GUa5!Ppm{J96tёl_3޽4 _7CJ%};7Nu 6e~pT|$VEs+}V9xO$}MͬVci3PoA(A|&*d~.@N-|%ȼidrzOwvgF L d>G o$6d>KC$#8Q8ʍ.-k\9֞Ux}:;.N;~<α8+ W;[ wim]gwxsy_eG,B}sX҆]!!\ v[+[\_lk*U_={m{O>N iC:NScw6A3n1~ ykZ[n)o!L)웑 {V~_}lgNa뾯I)"B ILTa!W1ˀJ۾m&ut>}* 9_b0RKVw Y3so#MR\T>#/0y %Pr"d'Cy~R>K&t*/(0_pa(s4x:ӼQ_I,A冬9rWEEr!d28}+!^:vҵ )iޯ_ qRd~ٗ ) d--2>|qrn]_-C*;Z|/~ dk-!p}beYg/G莾m)gr3Ԟ-w~r#:O ۪d}m=O@fWю d9>>g:=߷!߷aI;9㕜wu.u\x p#p[} n/#lx{ ބO~'|^|gRzj|?1vNayo`w(a8?Niwĝ?ox'J {NWܹ`{"юrS*M-cњMrl8)Sxd>Fg)}l3Tߓm>,3po9cy׆Tȓ|W+Y /U 7y_N+`m [ީ˹=O%WR;~+Qhm/o{[mrpY&I@,¹osʅo ۵z ?w5x*bvq.NjN[ͩ>WsHsOv"ĮÉG'+ȟ7ˉG֝%b +w#Fn/ 7[Rzχ{(yECKgw8+>8XͿ>pB>GFx~qŎ]PV]>}Q=naW#ګ4Av'Ncj.na>yWsK8\dpӕÝ+LE^f#Kk{?BŻϨ\# cA3,\e|p^>*|.{ەyZztCݜU^N>* ]/?Z ˤW_q#ދ[i/CBZw.Ex;f!n%l\'6e0sqLn%O[nݔ7mnwxo7JِP~|>{t1?֩[k͐ToЏ'}R'_g̉i\̒{׊4M)@8(MaoVO\Hm=Ύ SE全-y.9.?>9Q8Z%}:1]aNk6qIW" \m_*g6 +r-6{z gG)Ìӑn7x uþh}5CNM}޺{sxh. G^u:O@A(%?q怢fǎ/'לm#N.:W wy[]k,cZo~3=yҴr,][71LzV9g컏7TbqoXQm >I~9V}#WK?sW k=ί>J-l0hq ,Fd&r_lsEUy-)p-"[\~UלEl=6Y{x ]nL~W#YܑӺ y׆+͜6 OtGO^޼NԢE|K&O}͒vؽ\+EC!lpz\>LŸ+Kϙ8ܻIe<pU!M_YaDJF͎`Na*,^k*@I04mp]jx\-]ܖd(ӌ#DMj;>"8NeFM:4яOs!f8pϩ {N>meu{D|'#$ϡW; #wMEz{֦xsL>O0)Q>PhǙ7-U~iz6KO/w/Vt~_?V*`Cnln&ϴ9e;׾q`jV̹: p7n_o;9d{cCrxf^w97c+&g{صORxxs8ľ; |:l!=h"E (}Fiq_xY'}|,qR ~&Q o TOLQ8QNypPu3x3OӬ༈grʔψJ_#ˢp&npȉ,(G er#pgg3}$3:#S_If+ql noxu)UYtiL, |9S cmJO}ap lcW4 u1/{9\)os6x%%eKnȝ%QU$eͿo@xaZH w*:m^po[}0v5wW% m(#fL yP WpM[KeU9ޖG' ]Wkʷ+ff_']_|w .g+~>C>z%yosy WB^y_].?4>ͩ\u з }/|_(|>s6da>O4!FP#ke~=~6-Hlat ;?6l;;u{\GyQNumjj"I\~uG[ߴ´,s+?1cbol7Gp 7`VpH#qg\zl(He2u NOwb??H˛OmJgy*iWxgk`WF.~Ɯ|F2s(򍉐+ Nq;;%-~>)apr6KlB^DCy7'xGi} uqOS>Ƿǚ7۩GW<{;|FF&Q^m] !Vp(z7g}sI)}4p3m_Ky|I N=~ʍ .n=~QyK5Nc|QU9NYȝ>eUq6)쌳-( ܧCr2.>Q廒YT1)ꔟs3}9vşsr}*a%.`/b"N~^4ZNcy{溦U 7<5zapEd3 m: p Wrfb&e 0ؿWd# {/5~aAvf**]oH۽(lHOv|?7FS/x^#(}yHlk }y)FxmT"Eg)Oخ1̾/>cY~*<ӳwq@Lm 遗 '%ȟkHy|!bό}U]W΄ yq 48SU/NJOT:Yr(}̏W@_WUo.߀|?B|K[ 2_z2ϱ.KqimXu)/ 铮B_Zy̓]˒>{~ ԉԣ|ǏL<]p 7@8m-1 jęYօnxƣorX|M?IgwC_ǔ|^7!Z3n_ 7BK+P1:+;x_)/ pn$d~rRKO̗ZaeX^9u/T+>^2I (Mڗ"9 {E{g NfXw??Y=8LKs9}_߶VZޛ#G\iY6͕hgi1:4n"p̥0*S{˙?̣4p׸Cl;hL&tv7)d}I BU|K1éODwlT':u7G6f fBwot>Sǩir!Ntb&9|ߦGlܦմrL-~?~yzxRv{NҮDg)$}q+y{n9wyqRNvT͘McIk~FA,n1^1Id(CPՁSrYK}PnjPrK[ޑ|Rhϗ[1c/^)0/mRҟ vN O8R9qi}mS\0ؿ;O{8}*gpxHRWk` mǍ?\v"W=cO{x1ࢉ߷g<?|BG>cQx] yÎ;|_h\‘JYt?ݑ>>(w$8NoDPv.Ñ<3+[o~v{xioZ[OsH2nS,gQm>賩z>MOH?g;a-}eV.X/fx3iwa/'/Kxa[>% ?L/VR)5~6҆:S--l("x0Nys/Lϩ;nh6 -}&9>/{)WIu_D$_sq̚sE?ޣNO\IƠk-\ Ǘ>AH aҳ:tH߀xv큯Lk1qw=gҺlShrk:}vV"H|WBYĮ/igjfG\*Ӯgg!:g\O f; OyN >ƄIN?=EѸϐV~iԕoyy='ג8T/+YrИB?&\ƞ} \[ǟ%ZwYjORzŷ'|o_I rXɘJBjC6J%c4]oÙuQi?m9~6?;g?VxeQ셾ϭEß$^7O_s/?Ě9q .H_r5u1"> +wn(~?2HӚpwyp_w|[S\=K3 N: :ф[_; 7py~4I_\-Fu| \ɋO&na;YJW5:s qVC^sqE:Bhx!ۘ|*3xB)+ѧhJo!}Er(©Zo'7LPt1;*d9]a>&쨍/̄gn Ir.cEk5 _y pC7n <`v^e>W+c>G xtH|I*}]8KI1PsrAE 3rWDZ!y~|/Dd?d~v9ѱ'=7s~ppgAϘOy䮕ow)G]Mwp!W^LmTu!??ӘVS~|m""Ӎ &M~ڷl .ݹg/ěB8Cԃ| _jٺ8-Ң9{E L4?ysr:J@ Lɯ(d}^tZ?m! |QB~> q:3"" # y!I4f5u0d 4 [|_-ƌ~cT}ك]j'?.TZNBs?@N;Ûg=9܎8bZϴ/[~CPe"~/*N>9'5x'lU/S^|>%ɫ ;[+z5Z;rگ_HmHoOm~4Tw\VNZyoH W83T4Ԧrbܛ261:l;YKU߂_7>g>{pj -/ 9~DǷXm[#cC_[ž7 \M˧p]\-}5D:2*~Dx<+uI0k?%R ymR|vHƸ̓b7y!oJ7V &B

    cG7w1)[SZӜ }:nŵ$* ;!䞤)o֔e !mZS~|7S>iY_,) ZN{Ⱦg{W<-?%wPwH;#) 1!>S<6dꤣJ<8}އ[pYPn@8/|~];srWӛFɁ~y˘] M3LS{a9y|$7TَkԞ\ieXAl|dԆyD7D8?LNW)=k|oC$x{]/*AЏt;&o27: .;]O\1ȟB?KC?9wvSnz~O}pST|7S9/ǭ*g׿{cwYtg/%&?3&*|M:%z:w퉿< o;BJ#?e{ש#&J,K n >eDZ~*x6?t>kN/{LA9}ǀu _gӵq!p5|DmQ)7]S3áGS.%'~;'? N3z=>v9国^9ͷ=Djv|jmL#P/)>sv[Udݝ| 4?xt44n0)AwKIM?LFJ򚶷_knl QgIp+鐻gwpk,ig^Owx|E}e Ow5eE:4^,Pԟ%sC;oS?RWTE9/)T> ^uq=֤x:w!Ym8'_;]upcˀGa/E _vK=EIg~[ﳁ=+ixwrZy wpev xuxo/t"}=CiO>ƟO&旝3B+vxC$ެ=Hq(s'?VMM{2 O(T{sҧ6K#(<^>_ڗ|,(pφI#?Wۿu~7sy5"%'BHydT.=y\~B+7 ϩPu'gK* o=#qNv[Tޙe^IJk^"\ WLQ) +D8ތưuqpM o ׍pCoU|9C3z7*8+@^xr78a" t8/Ě=@mYщB֏o8Xc>9>WlmO?\l NbI{wt%7)q)p3wTv!m_S .q[T?w#Qi c:yN$-p|CpBGeס_#`pKZResڽ7s!Gm2dy?-q׌07:O֐|sorvY@ȳiǁ̓,% rGl#|YžĶo96:~. R8czexMNo w]~0]ߝ3#Ls9]x|/zN>љ{~Tߩq,_Ed *|zwx_sC?s?V3ʤ%XӾN;s:F9{jrg?̼:%`eLp}nAF'3N'nx*|9rȼO{ס}~~2Iu@U'?NOw~:lߝ'אO9 &NnQS7R_ y&!&$<%e1W=dmn-pK7Ě(lp6eOP|KQ5%_b\-,_u=(|kN;\ }ps={kq?^';w*U>h7< vJi/ha{10;wԿm#sҶهo $_Ćۿ58lNr[Gڶ>?mxz[Oư*e%I+D~89cRGmB,目&;dő~JfCNs8,Lw[}[5Z8"~9MK?,/B>20:^6\ \{/S[wp9ޒn=wt.tOUyN3Wܖu%\$ ӂ9c-Gmeq AUCvi\_; _n;_m֞-rJu~ǔ+᜔!7S2fHVkrPQ!4R{S:!3ogn}q׳=7L>&p{MΡ!ӊ5ɲ#-O~&[{;!7k_)'!fpcJK=8O?PٽY'{; 7 744ߛ-E>]V;W9,,NC?WO|6ގř{pw>(:)&{>v~eCSt8^vغg&7'1D=[p`__nR\=7pnK h@?a\8wO8Xw1rInʟNKʲ4>d!.?q}~_7o_ts46KdЀD,^u)?@ne;1`̮ ϼ3|-+ɒ -_z6^coCvl1pM \5zGQ-l)s7I.E=AzxWyTe}x'E^{7(אnv{<݈ щ#N8l_ף] _(̇J<7T&)|[7zeA qfo+&u/7yx^F~ Ek+Ѵjmx鉦 h Q:䲽Ǘ3&{ {$|mޡNWZ'-=rs<nڭclr|?*pY,(l ܟye0Y> Ӑ;Jr/qLW w"WR]PҗK>fy4ϱ6ܖO2XCn,OshY#u{50 عEpf̶>@gz'&ΜN~-pz:5<;+ېś _A!-xu;[}s8ڔ@0>ZL8#\c 4~>ޘR-lܯېøV7?W$6a{-y.܇>C_4fF}|*ZCLvbLaVy7&C"lTsoF].q|^U .>:Od݉v;wvVW*44faEcb7'eWR/7-ny>:`jpZ3N>cdnƫ';rYJ \Fr^ؙ#<Lry[THg_{_b!ëG6nzxO#!zu$?gO+^ɵz!g(@n7/kd΋wg!v99H6+j;q+pc}^h _?xߋ8ZS^{-l )O\n>8ԫNNl:L_ו—({{&O̪W7^3 bI֮-.u0_7a3_|50p[eyyf(w{ Umo;~gNWM#(-~;?/|a—/n1{PоS㐿C3{W«_'}krK>pWv0h_qʹmȕQs^ýSr_/^C8aY|9O?|[ ^zC\W_?7gG;`qU9~^x4iL:p~j31ޤ\]u!_KG7qs p<}c;3i4C5:'l P|DE}3E9yNz^蠥kt ~2<t-cp y?zdy_n vЕSEK;q=뵺Enku9䵺Z5oN=ߕtY[6)A} p-Nk\+&LCVsiցn~ZYkyu6~Q8u}{cy]ߺ- |G.נ5a[^(-9'W)ckrviuAqdؽ1ںFd1Ua3p's …=R P>OxR=٘zG̔K+ܣJ8d>oh|CLVekw״÷~{?b$O?g?4W ]~~!^K>o Pv7c/pcgX[H'eOh+H2pcc~ p {!n3pb \G_j{pT{kkǩ~qѷ٤) _|.2^[{8Z7|!v\YeښlFO;1'o\7?#FY8]g*>M> YArr;=VqؕT@G0n'iCz)QEd?:2h^oD@9N߼?Qܣ6+W+z+DSSo*zѵ91OuWAvvǔqTn */8|GN{nT\+3{#W 'e@*|w㍊C=q3~dp $_*)׼?V#C~i"hF> 4=\=KO+{>mo Iv(&]+F~ 9{+'^/ ւҘlSG]mR/OɢNE 2'.hLnY'|]r)^^!/oς_ +>?BYmTZ8(ɊW_䊒<Yw}HFzibCh2hSlK4 AwWy-mMWrxH)wߘ_\K/SEэsEs]_s]Ӻ0䧼~ Ow5+O~}s7uMm:8\=;*'f(;cnRy7J4 __U[i>-Kq5Mϸ: zs ov{c?Q<#83ί襊^> .}WtsE?(;EoTt+9G_}.Z.jwittsNE8,1~wp=<3y^z4ٌIٿ~WH=tu?3+| \_'yg!g䧕s>[eE7s-_I9M绊=6A_?XޣwGKs_꼿̛ l'ܼ5=mZ?Tqe~8{`=%gr[ޘ.^?w'pUځu~,wQ=uFy [޷ &ʛ{IX: yuaf_h#^QfU$Ŗ̤N>'uKl>/׾p\|o"|9hRRt Ǖ6ȗ'4 __FJ\g{'Qٳ8.)닐'unX$*UxxydR_>~:a/$~\$L4/=ғQas+S=]^)zf?\<E\8S+$n_փ_U˶˯=>D%fyD1$KGJz ^mӘ3%e*Kdzo:L[o?答\ҡQv%p:V1c,cƾaf,#f3"Hٲ/}W$$e$B !ZHDk ?}sgw|s9{s,my+>*{f%xmI 3P)s!LoK]Yu$vsﻏɫԯz[:Ω¹K܄Cޢc!Ǣx-OCޓ-7#U{YBm=O&yyp+Z3~P?n&Bs_rzem-_Yh/"oRD8H;F("^V{]w@>GsrC`sLom!"^;u:7ϜӪF]5|y7|pƑQϝ3Ag3;Fؘϕsep 0_`|09$l Q!2NӁP[W)Qn P8_XJ&ع guql~'K|^MҸ_*| Eђ}Ec2aU;G\ E+83 Q?_R9.TI*}|=\·^6X 7*[E`Vkd\s ygmq yJ4|͵T<^m`@9{.ؿ|ܫKO*k.(㐫smu+%#Jxfߠ~.irě۬gOwxJR1c3VC~)C-һױ 볹?k˦P8,p­TZ\e߾_.-M_F߱O>w@(VuUF_}BJ{y\Sn/s֎UկPl)p4$f\~|el~Y͹ .z~^]q_ɠr,%e_>[bz.؉]IX^OKWVNl*ЫJzO3w='U70՗>'W}N>':/اF ѼX+Eokvq?o/yzg{q*>_~^c..dfYl6/}CWWס5#[T9#~bYzDZrhbl-.+cފiN{e#i򳲩7J SQ`~MevY͉װ\A֗OQ=d襨1w.'yMxU 5t\M y I+z<;7Ɠy)o#yhyP ~WΥ.mJyWTxRmr^q)_wbYǁKÌtP}0kU#~In}fDGl{ .mF$O ܘ 2n+ " [_S$pK$Ui*oV(;*zk꛵i=۹yW'9MLͷ~ldG^ȧA%7qjs[ ;Z7xS9NaFMa|^ ⭯$JEdw|}s<]#VG^| 9*HWUi%k G;_9yobů;K`/Uy𧁿Lw_ۊ_:{T6yw:n4~ߣ}qV -i;Uא<ѕzxs zU:fYe7.v xG5edUpᔪSa~3q5 m3U㪹s&֛32ȣx;1>w9ǹ 9 ]^UnjWZsFt[Tv&c0'gy? >uB}9$ȫC?{~ quk=&m'87.Mu6p~3M?k?o.F)ᾭ.Kp/a}0aWq__xxKasa2d,Iׇ] Wi_u3~f`ͿWJ5U:}o -;f~ zd?Rґ*#eGʼ%RAy sBc"THq5{+4^#WNFsϽOE{_ouנ좿 g맸pE{Џ~>wotמQ&~oG(5L6oYo@8jm䵎nuVsϚ! ^WϜ1ۼ'~\?(l[l)ssnew~9 `T}S"JpjO>A(ySs J5J-+)-u=-(<3ZZ-u}-u+Ek UKjQKPxb-k~-ZRZRye•kK]q# -ueԖՖLq9i7x{ڲ.j}k4A TGנUˍ]'0.ML΋Pe1kxk+,0ϯ\޷~F։vGX_F~.y~ l4%$JkA#~]dK潭 q>5Sg ̧9}K׾ ^qו%_8V|m+=Xum;V9W찟aS$U&qRwmďq8cn.N"ɿO]c$ qv'9LxY~be/hgZv[>/kOV' 7NO'z&A RwURoD+YRD\G4赢ZV>M)Vد[x 7~zԻԻ] ޣ ;۽x}PP\$Ӓ'|]濑2$F^~! IrL~A1Hֆh7;q\A7#n7}s>`5x#HG#趎4ȻMnlKЖSs %wʀZ󸽆 s9fv5'Boc5w'Hq̿7!Ŏο7k!ŎwOA o?(괩s6o6D6'mfR?՝ ݥ:|yyu ]l{'x]|K\*xK]p^]-_bGabFts^w\y`V;옼{:kvy D։Md/zx'\.ٹ׋ͻK)ɥlަw^}-N}އ Q:e}L`w`} _6t" ާ/G N'gZֳ>{-ܖ˙qli}A  TaJ_c=XD4b޷9r/~QaP~ݾF%vĨ=>M{oȿ'Pg*l+K??1&C3Me/;@u5e wَh&הfǗyրNG1?+vG~QmOK;փAnk_K]$|l+ʓiu=l]g~.`F6jk$[OL6lOkhM|._ K4D6"ݎFûjC! UǪ7݇Qo~ZĚɓ8#~֣v,<zI^R)E[JqAe].i+WnO;hgNTE[:27{#Lc'8NQ v\,v "mZI|‡Ϩ;Xqk-0{>}=7:1Zb'B◉Vc? .r>G> n7QvxµئӮӃd9$c{{<ގ}^_ų\(%Pځϛ?Seu(f NyO*>O?RS3FqfzcQ@+9[hm''gP&32fqO7zGq +8+OCl-gN*si߾"N 'Do" LM,[E6i2w O5irL|.0?` 0?SK+5GŹ8‰_V;0?teW7>'._UΓ ҁHe'GFpt{T.}I8.~gyC"[%ɾSf1!Ld0Q 4#K!$KLHe!'cЂ-9yy_ssg!Iz mE[~Ʀ9ٷ)]M.D"~ѻr6yw^wM{_AOa?.wc#F?On9'Q_*<%)ۊ}*1>*Ley'.RX $ޚC!mm*֖I-IT Vm?geJRr9A_-C]ISvow?;gwxﷵQus_9hkh4\A"ϓKIо*@W2i м]}@)_&~ r5Ӧ3T1jc5&쟖;T޻J}p޻z7Dr Q`\ n ^caGϦxEJMyhoIwEJ6ձRáw#wvM}~{S$G-+tYj@bx~x|ׁLom1Y`c;}{hb_/Ci\b!\6V~_?sĎ5vP2}xY 5\y-$ <৶Ӻ v}RVOE}_u?eN~r;$8\ӷ{A|y<ѿ^Sh71|qX{/fT,:?)L</AmYŴy+2O?SzKh=Fe>^Co4O<皸R˝sWr6[JYP_8TlU\^+k-7!Gw{F\|%O$~H[ńy `Cy is"m}'΍sGYN(ˉiieӰ ʦyKm%-hˏJMhyE 6KX~c");4`N\ 'S`~`>=Ar[*V9 {66ܤ۰Wovo4vo; tBނޠS}@o},N_h{;&Ь @ٹ 0K!Z7`-Zo=w|jwGH''ƺ7=yNAo?| xo71>]X6J@ؙӔؙ(<s<%^ ;`cB??UЛ<Лz֛!+_<Vg=Bu1BwHidǦN`g~{":{@W.&5#>U;CzG /|IzXK.nc7xWY`Y!Nm eyws8!/ 9e#^/8G:a;E ?c1w2^E= x]{T|NyZ5Eٙk^pmN"]^/K]gNE>W]FH/{{3g'xA|9㴼Kn B%q&]du]Պ,ֹsΐ슟ߡ׋¾w&ypqj6Z**Wrz ^/Wz,֢$[c~oX06&ޠ&4' ?'KnœQ3knl@.Xg 2@]W?)77H]qƵk=*&^})pVþgmAoٻ*l,cX~w${圿sJUۯdBSi,cH3nrC3n}8#A)M69tߓ$nQ'X{"kY?.pW_d37pMTRY_TR=j ތt<~N/JYX|ʤoZ}J Aq<*fR!%q! nˀU d=rqm+`UӮt}?jE7>߳WB Wyn1ȗy`1ˮi98iw])PϢ-9c=N$xprc))WTɦ_ʓ#LSov-U@ }&ʁe_V>\vJn Pr~xEO a&[\e_2{A7A SV88s Zk4[ONU)]t{/}y.(7 ÎY?TGt4~V?RxsTd_z2>G'^`E{<5?m Gr`,z(+ZO|Lj 8iik9Y^d箈3,^o]m_Y +3`f ~zgD9t}#^[`Fd`c<#mMu2XΓ{gS#ĉ1x!yY:/WH}O^|3]g cQCs] enC̭G:\G C%?w\x,1'%Cvsl_>A~5d4mKOYC/:~|?7uiE;\~i=ZCΟn/T\~Ot戯' wq,ͣ#Qz#I~B3$9_8Uz> y^?~~ ASY›6sΊ®4 vd:@^Lȟky ?tVxGwy<~ao8ri ◾2fKc6q}SjD.xkPz53WC}~qh~c?>2ٳZ"( ,q#:>qץ7Ҽ./? zNiݝ_F$o8ۇz\6M4M=oJ &w{bk:χB~y,H>x\!ojT^$mӤU:KYrA-4(!@ qG}Ie<S80l֏"Emib~;j{IgٚEmu^tH1זǧG'obTG?IquͪV 7}}+ o) }%:5i-d?ANW M>-JO9΁< 6W(:7wKxVBt<o5]3=2E0_Gonaw`o=Vuo#F@u%as36(׏fLw~V}{# >pUizYV `ܣUyoI67>XOn[ GxϏL^A֬k;G:_}[y&`X>S=9 b  aD9k&B#c@jy~d+Op7 :4 p򵲪ݭ׺)6V>Ȫߑ?]f o ut;LJ&=;YԦW8kYthg`'8w>d7o%thGba˷ [OO$x's#4J r;IΞ8=jt۠U\_\dc0?X/7^c|,tM|k/@q]ǔ*j{0G&јKa^3ݢ*xE|^4s+e,O|F0s<# V}(Xpez<^9^Y{R<ܺRꓼN ة'>OPxJ/𪀗ԗOdUяJ](r3~jѶGN2C9ԊљbV^9R/| oHřWfiQZ(u&B/إ]_6*C9<3q{)y<*ja`^vo),ǣ[i_]>ƀm;wP=NB]o.Z"EЌ2 ~43vsm#ZNb]3<"MIw6ԼrOzۣ*CY/$*:A5 0i8+lwrG dr$[V5l9Ke?ɻ_8̳:qLo űD d_2N,TE5xnKD:EGٕ\u;D%R"WYjoJ2 d -H!؆-d>,d"$X^->3p<9w;w{˸ {ϸ k3_7$ 砌;ܰ(l>X܈(80%a"x'U:"x*-'cu\Idk8#9.WNsac%lPyɦ俹;<;ώAg.R-ncӸj`d{2?L.pwi9<<==m%~p7_<<:l k =pY5ww?O/>E)v9 Z>R^#4suG.*d^,%/T(k<\| GQ %ʩH'WQr~.]}G9724~Y5};FUJNpp>0)pΐ5\׃g* gϓuYP};Hw?޿QOe_KW'J-?pu / ?0[#kfex)0ɏL ouMkMqBی5`&zi3?o= x_uz ;!N[%Pv)9cKqpqL4f p/CnLJׅ~^|͸儊70%&: .~2p Wgbئbm"u6=ysx6_b|V ӏ'ߟel(^yA&J,A_)eyknj+u{y$+<fxgtV8Rjp=ˍ!/4SAF7~(ve,~;L J?=4&E]wxW{ʟM MW'EPd79CӘw %l=ҳl%?qUqԼ<ݭCHw sQ{FqKN / IWCJ|g^*pG^6g"ĩF{ Q0~1ǖع\2ϝKgZp ;F)ÖcW x+(2+a/1;оfZuo8O/mH{MáuZ0"Oi\a?bO2V;l?-:d3ȼ&q\My34]a`]Ԁ㴦4/6 9c#ՐGotI7Xӷ#pM;Õ[u&}XS> |K7>37NP.~y_mnhq9n-ᾂ{F'7xoӝ̴4|7?IZjRT}]ʿyAB.3]>ZN/֧;(ﰎ 7'Iŝ xM ބo2+&oEE }Cɒssf{]13/O2uhr >a˰ȘMU!o{7 F?1I9 u>也~NwAqa$sT>Ҥ@?g0ɗAʓC7H0_oDTn$qBΜDsC%Kx]N7-63d IZ?­0조m F4J\^~e| ʏM{\ogiAO火̖[?=7.P%?77JȼzVeg;%BSL܋ɷg %QQ?9ɗR.?~;LfϢ,6{*@^>#! î9."/>(,'4F+|\g\QɩJ$lWDe!uƼnؿ%.xϠV*JNy*J_Q|;ީtp;TQf7ZkK_:A 韁Ug9K{3'@;8s}?pih<d|+7]g2Po#imT^psYAnN?RٚŖBelmUo;Uޜ5 [V&Cnvٮv.:],7p?._SllWdz<팲SlWZR*MSܟ3ى5/64^pm;p&G)\v3?g.po@.&Gݛ)r3K9U+O}pI]wOBL}(eB+NvC.79skW̲*u+I  ˼ _7ٹJ#2Loq#/KϪsU&/p>#Eo^_|U͔ݣM,n=pUZ94'tBu}eB^u}ӎ2w-{ۍ^u ;L[*TH!sQ6;a P.I|X3¡y <%ӹ TkKW8!/wkwM=FBmv+{8|耲nB-oHNPr#C#r=qƞ?~MdTƤ?3!8pL]O&ƛu]Y\KnՁ`^w' &|cD_n I[nJ;OFpt勵K3`/xm7JUʐL60fDɳQ4­Sy^kd'xs[l .xT3@uK;W3#NG%X/c^9>w%pgB%=9Teͮ.anJ#McLI%l_Ջp vxגM{- {p?^I,z}/^ _Oɺ~Qt*Fy+{^:>gpns8ܸ$>cx^臑@ʉSšRsO/_y-٩O֡yBq" ?ooJKT8jسiЏS}1}cĺR ^} =g^&e,;YQHe3Y_H<77U /mͤe?l[ֲp/9Y—:y Ugs?,=}O $vgiCoR/"eNAKo@D$x˼\? x+TrzP٤?Haרdܚ[!fk<5Я:+nʗs=oA23bOMռ;9LXa*q *@dlS$g7NHFBqʍ^fpVس•$~xEs8Us\y -ƚc,`&i{CI9.s"ק=>o;BkRgR%VZ6j\ӾxF?HWQzJW~V2c6G>~? 3SX -&W9XɒfʖpOwpZY9++qzn%74 Rr^+]~귽['|By}+u&c>jUw$.~;-ƴKEM;^W:1=v0Qјd|hxU2+xň7`UN&ݣ/^]lK~*7/tU@|BЩ)£* _pB_&Jɫ5f}!kRF%W[.U+Lp2=[xk^>v5+"k ^e1vWg'6ex!`Cmϋϟ ~(|9 fQ"ay.%mD/?vsßS"=mx%M>O*xuW'=g4siJW?[)$|<c&7G=| ? UyI>ˆqVq^G0*ЧtiMp[L_8\a̼.n[,gGn$'oL?ҿ{.`e3kA Qw6 V^9 18:oHsV}&g|[Ụ̊ZwWO}/^s?޿1x|E⽌1iũ6tүU%SXO4'%]%}Wi~\(~B }A!%'ur$+ʁFQ'G5eU\.RCߟA~y G!?z.\o)+doK  7"9wwC8~9!gJ?+uqOڵXm^%)!$Jra\*q9.IN$EBI:.!KDǔ\ס\Ɲ1CJ3R_r szoy};־~!-7777t7>&XC&4%>&* S)} UF/77XoVS-a}i!>]TbC!蜯y-xBn)f{ެq W͠3h'{~9m9w pD t4Ro>c7msM_*~u$v~ɛb5~gu/g=OrsH_ O"/g_4Rdmtkwoa] z)hL~ EkR]x#veݟe\4Ww +[#N[=Gy5! 侶BC/sq]?Tz+QM#4}i|_gMC-A.XJiƑ*+zf=Ԯ>L[4gnt33=ҠmowwoK |1F-Mր_H[~x\[/%x1I!w#h} ~ bދ1br&Yyn \/ղZp/_ǪwD\]C[[~=978XD~_z&=뷺ce?RywFojn>=}}AG# 6$A_ޤo .M׉tA?[ɇsosYjö#TG+> ([X}.{w@dcKs-qxn_Rm%(Jj(gR ְ}:FUGqwOU ՠJ} ~C|ÏX]I)ǀx{?EA3@ٞ[4r[}_IDŽ5s.w3{,tAUѐs1 2>ܩƱiޤ?|I7_~bym&T mrbu'.w%X ~/nΟW|.cU/m&6gUUj!gu߯x_H; 8VMRw*k'[3K7˷CߕC\=H;;?sİd;pQG\=֫T<ϻ>'.2? |UV-x|ggUXﮋKqCMSǫKF2>Şψ{uArB=Hu>GР?SXeB=q~_S>g)-Krwt}j+P^[; is*gY/(S!?*0o6c4Q+P7n /GkT}oXvoNLމ-M/7[7N\ T0X :ıFC_rāL?"}8c΃  亝3̿ yFd;_$S|Ç>Ȁ]=R(XxM~tUOb\^2nU][d2 |>WY r;|~C^A y?U[ C3ǘ2яgoTb4}ya/@~c $Jl5c[fw!XsS_a~/<{}bYԐJejk~U+QOs?op]tS3k@Q w?_>g;·R]5?~r2m^z.w ȥo]L7|z>j ׿bb(~ w쟟+~7wwc5ROɏsgh'R!q={H~k-{00ۘ,yO\399 yBc*IGpuvW(Lk=.s_m{Sn?D ]bo9ئ:N糣[PuսG1r' GJ&~_=+^1|s KzOlK>m__ ^+-?q!u"?&ыN0}z^1<20,Io S ʸm |˶۵{؇S^&ۖB?om(<_).\`9&s-JZOB/PKl{_OmcxG`]U6nQwm wٚ/6Shh Ki>Xe }iNК/ pĠJ"0ZIڮ7~JYknDr;pgo>3"~*U^h!X}G/?mgnGS_~0^8~_|8A1;>M ^\ptL3QZОDu4]g9pG=t~<ܱ= S[*V[wTl+-u [NTȯ6Wg9^JV{˸ uv@$\ dIw<_D)בie$A1AW1<\L1kgz>5㷌8$vEz]}}{=Ѡ滓̾7JV~kVz_Som]Q>ۅky>Գ_PcN+ڸP>G}#Ўd|YUK7uc.y2+ooofGr -F}P<{Xs l g= ߋK9̑2ߍ'c>},_aǾ-g! } aV>\w |3M>)!S;򼾘c4O~ƾGp:(H`C7Ouzs5VYd,J 46јmX[tvKWo!v.c'Zmj}&GCE_sN,+f_Y@~e o#-[8n4j d]G}X}-kVkN9~556.DYmj/\w= O˦=N Bm;#dB?jے#BkVTkMƛԷSL{6m:Ӭr-.دڐ]%#]t=[ҟ{?棾c?_:4ڇcUy V9& =+QuirTTwkG>ت5?挦ufzg~ֳGCOsDW?}<+6~hгw;įRz hԓ- {pC;ss8[H8/lLPrS  8jV]oB[_E:=S^ɀQFgoz8K˴9ܮz-wKm_"g-2>dOl[k+[On~JQ>Oolb}Asi>i fk]uV@m$]OHl( tk/>`nd gWPޓixrleNgA.ÒR)JR~8>'!λ$Anɭ&cEpרDB~/RʓTa콙>C|#.A.ՙ";otڰ-=t Tj!=l+yw>5VvAflTus9SV? N sλSs\"r,J gGߚ O3l?gN+wGP;ZEq8i\crz,0S'93d-wfZLfg~"ܛ>Y:n ␻ZzQo6uwBP&${t4׶{p/ͲIq纸 Efig}&He7wZ_?=աǗO} :~w4Ĝ '񷜀 LAw;@w"c{w]/z_xɧ!DPip%"1oxlLfw z)h~ol-;];_?b>W*\"rȻ=,*{5i'Ӝ^w~g[ΙO^Q ڴ;Lv Wը*F'p'cgaju3Co'856X,o_jPZϑ^Y1k'Fۙm񫐫GrQG]۪nЍA=m 13H{׈Oc: ѯ7a ViOQ ͈  :t#/q̥o;_*qT{kK Zd*2̆@[vwf,"+w_sd"KFKt~~g5ܷAt|naSx|oSrݬ:(X) :; (~Or>Z;ߍ=,.s;ɍ9d~Ǡ&Es tݳ.=O'xkPce{ Ak ھoSb~v%OeSsLSGB+&=o:{v5TÁ{Ѳ K_΄-56/[s#?64?gOF* !QzGGkfxJUs x)s[/G;M"='m!sc)4I7Vy{q:nOQ2_8_npsy܃~u2'K7''x+w-9m,-U-b?k=\o {E $ܸ}>FDH}HrӌfSU>&xNFW>7I4 1 * oyJ}U8Ư&|Sn9^bVмa}0h~GS?kI@Ah!S ]@WT )x\a h{jڈ炗[}/|ޫ:RuI#Y+r?!~fW> ү;$@v˪ ].^(;F麱|m[y#fiqۀ;lw'?NK8)+LxǣCB: 0$$%VQ@@J$DDBI;wyG{|gvw69{s\켨SvB~Rd]8;σ?og#ew 6ުxwH Ysw =`;"̙͘rǤ{ ;H,πNߏ $'0xa$t¦3`F` >dd(y X\y)x pfE;9 ^ hu6r8~׼'x/J:^ z]JPxKp*0/>'3NKq J{A ©.!yk4ꡮ99>o"7^_ք'_|n1ϝ~9ŗy8?Kgs^+y >+ZcN~%tq.G\&ԯ3C "xľ<.$+g9nj66sS͉M/2^vN}>mUqؚ0I%ɟ{2"/^< )pG6j;ub5$wWǭ#߫&AҴ "M9 5sB9Jd9PmG)}{yzA,gL N?W\7%ܜYǿKsIl%of6GکR 5i7{6^[6 '|{z흶.qq×s>G1ӗ6ܛTTw!X.݀<|7m:9{ XUE!ac!I9T-s8֔G;u׉~g sp↙3A>0H]1f,ZDxlLyVAxfp;cw5 Mz}\&K=gPp;TLٙ"gK|CaB'g^[Ry;} yς73\22O!)+x]Mrtҳ?]UyYqFe /S]v D\^Ob=92a ƭ7oS;p4ʋ ȇ/tkfKg~bȗ\ Xy9Ƿ;nnEy_W|dT3v$1QCI/9 ;ɫ.:h FR(,~qzd!} ONݗUx?BUo6}`rN;^bJ #5_7yH n'"3}eed9-]^SN|NzKH^9amC/H͗|)-3'ob /aYo$e.\ ߤau^v4鮣('TUH*R}1rOSg=;ov/MXܔ'nVǨ\Sp )pKC?w4MK8x`z7Io#0?W|&ӊki%00/!g}wxy$⶛&`v%]ؗg޲{ #zuҏ8:a!&.Z;RSy<佁y_Idtb=㑍?.7.uWhY6ᰋΦzm:8xIMc+<}zEa{k1'R)rx|3yOOS)!?.W{K?ucK ݑA?3闺X ' F.?7 o'<ٻ$nke#i|IGSCOգ# _~y.vGw%ݿ^PCP{ýhĞ QX6m.B0e0k%=33 KϨ^Q؋pþf?W&NqUϵstO*P|/ۍheQ35y޹R!z/_F<ٞXcW^Y>7Mv_^y+j*Wl=+οuGAyMIkxwɾ~ugL6] j$t.4Dqj߶06\5Zw9㭣 Z PSOڹ=!mP6w$ L޸ d!pq/\{_IaϷyWLq;z2~wWxmH5[?$.9IvN;f($< ěu5G)qBާm<(~\m OMz1u:s3vmWIjeǮV|ǾinP{b?;۟gYc|#g;\kB>\9 ?7 h'_Hz?A%֟ ˎw:':}??9H7xDzG(́W"qźNn \$.dC9y7|S6xS7K2˹OYJRx}Ueϫ|'_3;DaS8^+f=m_#nOo0?Uaw#x27p@$z\n\6z~/^@_g?mہutewC # iGÔ{_2!^m>ow!ARw{."7u$] {x{>`y#!A}`Z}~߻X6lFJ,w/J}/^λ ZvS#Ⱕ{ b%x1/V*oS;?ﯟ?Tw~ERl\M^`^A|TNk&'!l%?</YǺ|׀N΢ yG.q<㞭㺜ܶoxYI'b? \ ).y+ x9m+֙o9pz0 <*OQL ^^<*'ۇ_V~@>~! ?BsWt=6n:vy Q4A^ N@}`^'ȱ!}dsp%69{ӷ ϣA' ',7{^Qs=h̺1B֠ 'm<72o3dM+5/Vm*I#W=Od7U[)f͡_q$ '{g~R˟-SXSt hZvmWy w)p*U>q/s;:y0Uߜxs{>kg>LaL47-5RHbsssRvޚo5 1ṅ79~WDwv:♝N '#_#t;V}I|Y z|Eߙ( A@L N|X;Gz}i@K3 m~xF3>xzWiڀ#rF kd$03{ɥ oa/0??>fuӄ믨%76 \q}:\tSRO$ۿ9 \zޡq>J{nYcٯE[d>ʟz-\Oy^`M iNa)gn˘rj:٬ES:*\HFh㱞AO{͢ ֞,&]dXS]i4wФYK?e>gQ GxKPZꂗt)<@ k[re&O#7YnFhAg;lsL¾H[REں72<Į) ^nG.tox^6}^ k\g6M(VuJK+|}i.J#%ϣ ׀xe*u㟦%PؒHS~boN*I֎|sv?phIQ? -Z9e()B]v H|os#9LF{=>AASD>O-t9vv>~[ yϜ>X6"> ~//}n* 'ͽmM+01R?s6Cadccf 'b0PxUH(<{s8}u0~7,pqqq~ Mut2#C>Z1J[Xe Wr~ֵryV N:x_%2ORaؙF/Q)‹o3-kU{D^!?'{sh29_(-Ru{^5Pߎ{K;pA.Jxqʎ*w9(m)E;1*w7MyQGhkq:zE)G?'_,Lʤ#(=}`^ouv0yЈlmE󛽱.jmx6'6gx ggzPHFϞ $}_ NĬ/ n] =6x{⾟1+лyObh =1v>-;$x|;đo<>lx̓c-;J ?ٗ;F:H:_S&4<2\8|(Á_KX/%> 9,#]27y л }Fp3qxcW0'߾x77xW N< bc"M o09f:o++yRzw1SC~=+^;7GfJZC ߵfYE!I\ ԓ0_^Ķf~X=덽2u~("^M,lYr(r&rd<<[8eB9Sz@/>Ty=9AZ] {n$/'q3Bk?${ݬ?xH'}|3B R+uP+@/ u6&ű?tWl22ov#:ub?Ix_Mba'39KoI!I$ 4yP/~Qd{$*;uϵc=;%zRB?'N|嶷AZKj^_ǎԵ׌_fxTz^2VU>ϪS=)geO~||SD [Ie2: bw-g"Y+՜_0-8=NC$V QxKެ _V8"Ι毤^MT Sx>]8p{(m8N^W}|4]J[+3nE:ݨ\|J?z:\a[ԧK9燝nq9jp-f=T}?{Vz^ #[J in?NRt/+7o54X0ǎFBG{MG3\8~'zOl|G|_Xg^|VB>^ }]|G':0 C/ʉWzu&,ޜ˭8tK/sw>Y$H]-qtMU;}:oUޛ^_nmJHoomV]CgmpQ&Jk{j 􇛞pf9[MRg)N'ĝSA>$~wўT2BqP>ǎY [?&x/5OK=AF6{.w(` Ry {tqν_ qBS!]`YT܅&V]2 ']ܾx]ۚ ޹ !}ae{gϫRqCS6_Tu,l ԭ᭨H~ ⯿F~=H9zÌ wS۳Q7N{F~%ӎkk|}|7;\W~E7&@'rJWoˠv)|.?),,|وl4%`xWˌ}Y #y /%o`n ~$~SޒGzX lT'cݶa>1&_^嗲Wx)7X#Y;Du,OEs/ ,u)yO2G&}p7C^%_c3j`z羷S|_VoGo=aP ֜MIݲtRxGVYm/n^]Mc?$p2eŧ\7m 󯃋9~kǏ~^iNI9㢤6RU:8񹼆}BiR |믄C|ˉ'Ȼ9:K yfvZ n"du^W_x})k]vvz;z#J|ןe[bCt7ȉK6rfeCpx~9Wp G]0]ҋz%`r[`~Zv轑&c;y~,Mi\+ ';;w%=q4]w;.1@@^f\܄r%qNէ}fϋKouZ>sy8,ǔlx>Oq+2'/@Ѽ%Gk3p\~)e3wWx:/!qarTO8_<_Cjۢ]fTΩOT,~Mʽ`zS~E⮢p_qWxTxQ?:s[N )g'!y>xۺi/K ful }3ߟoHi/gz5[7-j1!G?0?ɭΓkɻ|>C^eMzɯt] yJ'~>LyY2TĻ ee`t~=YD~Z|k 57pc;q>Z /V o~WKl|2YcZNs(\Tj 7Tu遹=\Ьuɏx*Qpd>3*\X wTxS^ _U8&Źps(*U ?P8A+\BnPg)F T8iί[ P_){>M YY7~[ Vxkޣi)u*\Dj 7Wc^o V© [O Wx3^ U8a3)\A S_)H P)HZ T£^O Q/So)\. R+|\ P RO/* Sl)Wk ްsu=…~ǹ zu`)x83Iq)Uq6N̹?n3m##JZ2:Ǥr2~X2%tUJA)ۑsʀxiHK9{AOX`y q=K +Jme+ky_y egρ#HSh׃Eao5̗gb0rIzġτ蟄 5pvysG*?ER˳y0qꆼc.q1Ɯ99U}^߿xH!0?k \b?͡K2rqݬpKfexה_]CuGVb7M]4n ~-EпDy[^<,ϻFΣ9Oˠw6IZ}?Ρ|Wr-N)cת.!J -m UU͝~^<<)n7{ש{|va~ail-mˮRi|Ї Px‹ޤ!o(Q`#L`uma!p&_fS^/|2p=W]d׭Oiy@.E{l{0H>x9g im71CsN_2ϥx=@7 /ͤp0 <ÆSߙ˸/y>H8+eafrq:ܜ /s} y/ȧYSVqq3>w9jɼ=o>UoK%>7'xs=Ӓ%!ASK]KWx5Hm#x∷;Jzegl`șM- Y_%1u7H}̄p:eĔ^ީ]ju?[Q,Iu*y?Bc{%=Ыꔇ?[> yCY`c R𮓓@ '޿e_g=>2{ۄIMaɃW-;Ax'^]e}?Q*Xř2ߠ'~nh>q/{@뼇8rm~jkf搣{O)WqG^q4HNK|=IyYC9I47H@^[$GeN#x:,<0x Oe=篻}n7ή5|RWu3LL;l.i:|~^w|`p`ėʐÿE7*]g|@Ix ~ҍ`֏~?'̾Cx S^ix v>Rn|/zOl߯`}٣ee8ԽyB#Ü|y{5ESlxB`_疣^EعwZ~/p bui0'&O)?ޔ z쿸@oFgu}pF*ջ-iK\snU3[Jz7!AaM ".0GJT lnxK3~97T,[N+ IM ?Үܱ.yc,}q6on ժs}:KykI>7u\C>=sZf΁zUFo~~9xTK3~GcgܜdL=:;[Oݯo 2I_}$A ,)ʙvv8:QW5*g ~ݤWwk)p)myQ7kQ*sV)MmIS?/c(H[2Ί;ܫJf2p> uw/Qśx.#ĭޥ VS[wx=wPf?u$>W:#p%\v!;WbaqϭeVżp  U<9p7|Z)1Na{xy 6W7P 0Ϣ ^ w2xOSjFMLS={?̤u;'4̅+U>#"$E?!~~W~*&<)ɧޠx:`^]qޏ9:ߏyP< dCsӏe?SF{ =T=o4Vo|9HZgUߍ ^W9OOxpԗ1K6'g{MJk*H~[ GWǽ~ {{i8:?o 9ǔƦ~ ^qo3y.Jv x/M3K N䵶~˪罀g`KSφz|T(<=īY@G@q)bzD!"ϫO0{~U8sj*^:ЯN-j9<߂(g35۶^wHoRט+{Dgx &d8^#❫XΜ)4-f]ț*U"kJy޼54N?ϴ9L{N?(gK|˂mj=ʁ x<?.+1?-7v9N&g1.N5iBX,myQggqދ~^Gwz|X{I;9}}' .,BVjoyW~*ȻI{:~׸(BY{}x Ur]C߶e;m[ ,: GRzwG R[tmHʖWX-7)o^"7kIc?oN>o`AsF<1ny8g?Bs,VX׵B4QNG: ʙcHfo&A|,i*?o<v:> t26ֲ`>OlpLmsN>5e!tPEM7=(y&xW X[ϴ]u[Yd|S>2w\Ϣ[Y_[M8Nrq9IyݟP8kf2~/oqπdUb`}'x9ٚitWڶy5'29+{}S)EL[A|$,H$5GGbwB ;ǴM]({N v(uB{T,gXcꜤX?`j{5NN@MϊY#dwϤoh9>&snm[yBvzcC |z1Cn;po&n}M[Ugpf~3.ɢOUY/?}IoSi( ߴ8 ^|T!x TZ+-9Z3Dﯧ\6>{L41~' Iwi9-ZO(ueXȳ8uW/Ǥz47ɣ)qDW lZ;6LAJw%_dfsWx&[p1~']V!ѫEqoh2g4l$!{5Z]ʶ x sI`o ~ġio_~3t=`ǂm{Wջ(﫴s~ǺP|Oi9-[:}> -[Y1)xm2q/'|w.e'k׍$ƸRw:ιZG2gDwuQVCC!JLfa#7mpO#O6mP|i\J_Hy0%y6W(|WY/ Ҹl9?wi#Qa|>rYy0 1;Zlǩx.LI{-hRl`HwG>Pv,a (: h_?^ kYLM Lӏ}~SMT!39!HE;w~{J#aG* 4UVq}3/xա?Ky_mqm?[OU=M\^;$ݻ7)Ôi!Ś%W|OCz(\qD}sVǕ̸E?/e˾C٨xӌS_⪃Vע)Y=jyiLz1Mw*.SY# eK JgLytQY= 7 {77 ZU*޻s)o2 &-vw˷-c'[Ĉpojo[ބu~&^DQߜ}[]\d/Y2Tq1O4xؿ(o椹|"4n@oNWm v p2jy,>{n_ Saa.7Hʠ5xI}I(']fIm1|N _M?̅VyY~7H}<$t@'-Gлk0wű/zKݗ|лFzU\w>w ZŦ $D t~kSuy.wzE.B|U*l)rD.=/{ft~/m'keˈfȘ㭏Id洅^㼋i'g?"ٓ;ﳱ*dCӡs+/9AZ~,+,úE׏ ExDĻhᾘǑb͞<)qIEdIzTiPnObT/ݕ-NxVꞇ,y=/,/#C~?*oO)CX a;Vq_%_&x˘.7_KL"z*?Yi|Abe?-x9x!evWEϡ0va ߧRm!㮻߲$$UR&rBѺ4),gn6W4@^䃁%S[|+S}GvMXQsMڦNn-}鴡%IIMR5|_q\.Jdr(^w~UBh37M域E~~(NP Yī |X}쳂(|L{T_vk¼=? x:WkE7s_129PK 3-v~E?3wCĩ_\a _߱mߒ p~&&?9qk[2B^;.9ђߨqz1 \Oz"^9~c{V3%c9eH-’Gn idm &'T9"< 7vBy)<>?oy)"/6R?& y_ >c[kWlHx ,0/xIpg~Pl5 #:п|!\b'7T}rSp*Ks%w6' w!x`n{ם4̷._~* @sse̽ 7QJǡD;|$ L$[?kdB9%{"%a*9׫EClwnP!BR{!"yHc9i( ~m>Z*~;ga|U۶ f,+=%Tiܼ#$rg^W!7CA>QKrڱy=@saR(^;^R< :Ro7pIl)5mx/>rQi exnjީ;273J^{o&<ʓ#*#xY}>_/SghכS~gFC/5C;`>σ!>o584|*|0[ j?;]<ӫz5oӵ2y-w09|1<`uݢ\'NR_ ܙi {u C&C%z_.]oS78CU CUqrzӢx/Ә9DO}3üaS~If~N'͗wk,yKy2 Τ{n*l7~3~_c1-{)]RN-IafWzTBE3@m|׫-^Olr=݃N5>e瞹0(6 ΁*O}_rv /k9o\=ȿ7xTdp>q@9p֜|B^z_ 1ܷ?i] |XC^܄v!o3ueOT^?^8r?;jlWֲI~g{~{3 ;ͼz9˶L&;Co"J3?4̀|>[Z7]Uyp]:)ݧ5?sEFx#ޠCΆPv\bg|5|S{55}-ugiݺ[rOA{Ccݼv68el~)x t[i|:l o56dc9Rƞ,V;} oxǺp~d%:y/:xӉJ3sn#8es<*nIĝScg;^I^?+wyM c9=F s¡zo#;* W xK)ޱSxĻ?]Q_[I+yo;r ? x( px_@ 𮾀]5bYe>YDa=x{ ?>¯{ [KN|;ϛ-K,/0{q=Zg N[l,9ҥ!~^~lt}*7ﳶcc' RS?Dd?# ? ĝΑ~Tz7Ezm|+3ם@_󝈞+u1ڕ_,4ϻAF3ePa! U.ݣ^j  |@'(8`V6 5&lY%? ;ѯ^ _SGzW2e+ܷ eے{q෼c2=*}#-oYvC#,X4Ugd^djSs/Y?"{8S.Qai:iKVS7mˣ{NmO'Zt6(|\j@S(VE1~OnMSY;=~q6N>PQkFR0%CHޥe]ݲ (//oj{lO޸#̙g74kr؎R~mytFB+Q;}ϻpoJc+:yykLF̷[Szӕn?$gML[?e'/t &./// (τߑE^=c*@o^cÐ~W*Gq.őN\e$s4+v[Mz?7j'Uϛ><3x?8Kwղoo٫#{wʂQWU'Ȟ z?9ԍii\>=[xE$0ϪN_>]c8p[(ƬLʇm<NE.'YkOϣ~59s6HK^vx+Gv/?3 O7̛ǃٲ] 9L825>E-lu}^A~'x9U~>o Ӄ)wy'kOy'| Rz^ITk5K}NU<_н#EH> ^[Ix&1ٹ ;yUY;wO#1SK|pIOwӿ?5'+P3-`~^SMސ}1=y8j3(΅-kҝ͈ 6y ϳx_u^wvYvj9m=C;ɏ|fDUjT* !o@,.}(p6 7QC~)+Ez)9c赇]G"ؑx'B±#r-x-ols^>r>=G o&A9?&}+맷b̳fZtLG3zH/0kyJu&'0[{)Ӑ \ƅ#l?) 4Ω*O+7CsoLi 2+ֿmI0Y]܌:^!o*pYp;+‹I#n{ Օ0*nnXyNu(G>gYXH^I6 /㹘o$1T,)ov(% ߂x?'hעW:++뤣|)lG5>5΁.~}v/f֑=#G%NoxKɞWݒ3π l?+h[~U:?b-h[/ɟp^h*qRq.I}`>+6rLCs!{93ܸۏgL{Wٻg.As\zb`P;/䗝x^wS/Q…]uZ^__w9 F{ޥy79ۺ8I~[76e+-.`.Σ09xy8`a2oLR?Q;J?&;zw{M  (o]!#BUѽv}6E{yuyn w)[' y8}֛TQG/>„'g 庺y \pj )YL<'K* 2{meK}#l)6yJ}j[ ىO#G'quWUg"Vki RXx <:H/l&޲| </p8՟&y^:|^t yGe>~ y^I]>f1qw!>_ÝN8n7 ۛ;ږ"o>}L`y'<`J~V)$K0'^Jw3+CzI&`^祛`&V^yKa]VI zI۴&WrʪLPM gC2Aϔy6U~4i4xv. ~k<(j%/Zr0{v{sXc"UK3q~W9}>4 W< K;)vg@I! :u wW o!{qY2:ߧ>lLj^'Nޯ{^;CޏwP9}vϥv+'3mGGOEX+7 pi 7s+畆|qڴZaC" 9WvN: 8 |<{=9#pl{Jr۫!z_R<'=I׳OV˹"'i3h0O%~$7j[<99~Xǜ0Ϡ˂?l倯ҼuC|rT3CXt+I+n/n_[ǷbG]p-[_i;ڂsI*'ᷢ,?ժf#] [K NB8B^Bq_oU4 ODa[21~$^"EDQ9o (,:X=o vUSXt*%hTX҈ףzM}e7w?wK#v?{u~LGi>.o?o{)hL_뛩 7{ Uo;C9rMd!j gKa>^f)s06C~BS9Vxl]14Ⱥ<-S@s *R^¢͘ 볡wlTxGO8¯gs}/IV Y RYEM$_4$%BY!UF{9u<\~: )X7~Oarg?/|ǺK&;;d}!  s]ybKpI_sHm=NJH}I7볰ⵣFN|v;{zH/<~":rQhc}>7<݆^uyiНGy<{}:N;(;l.n{kST!OV&L/x01ӡ~;.^fҩ9Oۍpy&q|*= 7@ƹ/V(Q(e/,l-lJrzܘFRhKY5Wl>\m58 l}cw$% 5`gl tIyΞx/M}>*.#E o޵S}I0~C !>f> Q6-J2n:?J|I͕|1WEnBM ?ݥEVLw%ᩢ_+}go큞FJ 2ÕC R BC6*mϐ=!'n땔9 P ߜGKFZ4axJ7 zSѓo e^w[ѿص_'3yK-gB_"iu47g% M?WlV~vGz=CRgnNnKuBٹ9Ճ4?8Ga4oIm64f=`}' 2sCb (o[y۹}2pgW px1˥i 3 7R7g-]K{!J(9Վ#,GfL޳o+Oe IߵϿζ]Y}UTv=Juٛ|hO!ot-4mmLֵ١isdݿ0;`@vqj*I 9s\_wX+򮖔?i)M=_D\oCa+ղ ) ^;)tj(Vp9Ń|3xUVȾߙH`;yZѷ߸`w4x7skw9܊2Ȼyu\pO+4Bo˝N~QF=^I8%=^Bn}2/4e|<~%AޟT`c̥sZ-m ioef/ܵoF-݁-r.y%^ͮe߾Zl8KoWqa^$iNOs~Ѵ-k=2ߥ.?҄aIѷd}iO*?.|Hm;׭8c'yvNoQ [b[wNTuk8L~ؤ|I&Ao'vSh`\8oxSKyG$.6-^oF6X=?qK.`UGǑ.%uRx_>8 _^!VG\\24Kxp{5[Dv`L? 8{бqq𗑝'n#;bs̘vods$0 |1./6)ߥ52*cq~7kԎ@oWK/`?iBܥYhlRQSͪqYڟB97/Uo OGx66\bSʼG]*ʯ?]_&cno/Pn('rtZH٪ ܐt*<8,?UL?n~'Xv5e/"\~Hy ooU6v{$"wVK<^Xŕb۬>" oW*pNeqh:Fz~p4(5p.I x)*<9.:wx?>b~Uxڮb[et܈5=w嵶o$Qyň`vz ЋJEX]޼K,?-~ҸκMP#3Ϻ{dyo[]o)q_uE^\#o*XyzM7x^>VRm?wl1࿮n|?Ar<*B-5Rah}8cA XQ^p*=N*욯WWe# +n[.*z_z[ OCiF}JwΧYYT{d1f6kmpP$Uq9G~UOs7.w7f o 'Aiq́9ȿAuF?Y~ȿ8I{l?gl nUMꬔo ms[G(suڼ_On݅aT3%S˓81OBU>3(F5H1ū {*677^u7/S{ײ>`= y{q9 ۹n*3 wR'7Xyn3\>k!;*^PX`WϕwdP]wk{ ^˅ӗ U8D;9[^lW!ڔ|~wv}}g7Tmü:w䎶zsF(2_z_kUp(v Gy4szO;$!9 FMh+7΋;Ks`K&ޱ-w#ywI?lĽOa;|?0A>뻟זBY T\JqeMfxa \ϸ|cPvaY_*ܷ?#Wsy^{e/ΖQUrGBGG^'CPoPs!_\'eȗxтiM"Ɯ9_U犊5/%{2ʮn+ \sZlӺ0gPX:oꁷVڪ_zu>׎1`[Ěsv]$'x~7EܖW}^S.si.2Z x6=Nowcmn(@Bɑm;ҧj;V@~ޛVnuC~A 2F h,{K͘Cb<&c7T8<ӈ!伖sGo׼IϤ(-G*9c.>fېG+9ەyiyl+'\?e6R5=;RmK?x.rp"m6#Oyrvy,d)l3xU9nȳT3.1@od_Hp8;pQehif"kg 5-tWo/-%|l'w,n^Ҟ#}szv3U /J2q5Io1%? Gxl|MkEORy^^[Uw]@~ƈu{tOS{^\X'-0?œ{ٕ=5G$/Nkt6/< f1c>pa,1+ӳmnCTIț{GҘ(l'匦[k[ ׫'FnonڣZaViwYo=:vzofx+gjOYS"jY綃߶o מ~:ƒuL^Xx.=^'cW7bOTF.YUi)n9MVvn/ű 0'?|ƹՇku#Mhr)> ~lUk_@K֖o e&k%؝~e#yCv%}I}?b9*~M|oR]:Ҍ(b^+f쁑Re7o}y{-7obd܇tR퇿w ;/&SWV/+Pwy}5MVJ-'CK+⛺106)yݎ'emqxU4o.[SSwoW~<=܎n>}|x;vm8y:׾Jl>93ҟm,@߅S}c[A/YY!?yj; [Jz" ^88>>ż[BNg0f)-;㱏w߁4n ԎwiKRs9K`v88gr_h}7.g7/0p ~ [ܳEPﵕTXehl7~43?t\A57~țs}mgXwxě†H>.v tn<5ޫ]] n?fhU8S/ 9y -7O-C/=U8^Oą{Dw? *z*Κ?8\^[ЏKEŃ7f/]=ڪ]}*Hl=:{er($܉yr/¢F'xy2U4ƔWNMPîHs8_E{1)ޞw*x vfj(@ވ䭁]bJߋy0>>xIgs ^* oؘ` _FJ3k ;'}9iJ'fDhNc5g=vmˮA{я;jm2>o~)xo/#J=+lmkp9O=|7r>:y}OM:{ze %C/z ׍~Sa>‘lg>fq9h@pSTc"}"Mlb#3E5#řD|33"u׿;Jy|huUvyT0x^{}4L٫!\g9;vg1A]XN7g~Ok!L[pNq3?Cq7(+E7p.JmS7{҆QgrOe|ܖƬ}z*D4p4e:o'*\l:|ԉҘVox~}m|gϮ계}o MHLo ׼{N'[$i;uX߻RE$m[cNb<]Iۚ-^^wOqGuxdq^^'.țjy9=OGg8xn;YmBzyv weza{[Sy + $'H^8am2 I+X< ?пW-xSt^cT>7?2m;GWu{Q+bS㸠eoCxoO]_u~`b]C|u}ל~৐͌20p4Q)*pp0!5 ?G> *oP: e]8৿ez E޹#♸`KO {nNiT\·+ws5~&o<EmsjLv>+~˽O^VMȇAIj;ۃ`xe.gvu꒲yO(!{ Q<q>8ڊg\}˙pH]sӐU.ΙtʋK'+:S%^/{DOPJ o|2ɫ_tmrӋnlV5(p7MI) RYj#sO0?+w _%1`>p6g-+M|ǽZ~ ,D uHd40*o$ǫmue}Ai0|Ce}c^8// H "!isz^z(_ C'I٫ePH9CQ $i|յt~~r58;.XߘY=8GyM#CϮ(!zbC/t!{0Oy1'!ې|S3*Q䴾%dkn׌. .#C3վ<Im 1᷁w {b$eC5mjfϰ/ w6xj=>ǥQ3ߚ6믋d'ż!Ey_Ե9}" ?)(wI_\cWTuV;)ƿ=㶇0  IV(O_d+=O:n(l7p1{,I9_.L+Ico_$A:)|#)^KmP=8+*g T=NM{NW Q<.n:Ҿ{ž;J},%Bd,"[ք,Y !{<}3gyy x&ﺲz=ůvx9_vMOE9YoCy \~۫N~{._y/>kۍ/ԣ}u?t]=yOy/i7|-Vw*ݳ\GIRW~U]rW'^ϴs^?o|I #civ3[fiIZ{B9#u |"=yv׷P5'i=?B \}+d-?қK?h\WΨp~7nBl ɡq" M:_Xomi/bozc_ER8O)\QW#5~UTL'#GS(-V x;sw*׈$Zt|y%iW8pe'Ћ+L:=|d.mq?UGJW#[k ,<&gr(1C^nO qR7fzO ~0]޾7zvaԖB}>_\v3"&g;MB:' wef;)ұaGǁW3 O9}6HBa_\zKEsw;uf _Dt\Jx Θ o}9Y\$,4K=s.1ț>|uyqJ2WN 5Mڃ)^7xJ},}Z*[ 7Wv'ךx_W|υӳ5ypH0r祹2erW'wqTvrWT`pۃ`t>kc55cEr v~5-ߧ>怒)\:/澕cMdz)>)i :8 yr4ב>+Zy7Eh= *' RZ\p~tK=it*|<\믵ϳw(J@`>5{$C>_\FspnITOt_8O 9> 7I ϤOC \u<@e]?~n)b ̿{6sn_G+ń2ʄ ʁyxW4 OύSD{&6#ʒ-.x,MFW&U evc_OƸMȫ6y'ՔR?T{|M/r/~;U9o.d%_}=uU㴖7\hHCcNºnĦA嗠׀Rgq'8YқCZ7&yaInv*S &iz7cumWw(7 o-I]im2֌lm5ךt$HeKyVށٲ2.2 `u}> i^f-ׁi_VW&?ʜע|׋d{O:S Y K\V¯+]A *1:B};ՐmrpO_!Ky4{BԶmҹX.;Ie=~'!Eǟ%}wY*@>:kݵ8`fY& \ g[u%IIZJ[E/mW>k'{mcw<̌%_T_C[Օ~t&E'lONΚej~+|XyOp{{rƂiہq9c<.On0/兟g߷ ޷StXظ%&826~kCX&ޘz)7xew"p0;{7G};𝽶e{/ۧz[$uZ|ދ 7({!ߡ<ޟ[|#Rn#\Vr(\e<;=|$`nwo֥:*јF'~[- CmoהC^H}0'rʜ\}]Ҵ JKzJw=]yHgV b7+b_x;|4|r?87sx9Kع99xgIq)*ǡ|oqR?Rq^kT:ZJ(z#Bπ3vq1IaXXzQOl'SlOўg}o)=*z2g,܆KԞ5),r̎o>{2-2k~ǗC9|n wUPILeGI׭K_/R>OC^+#vRgUF] xyy<}W}=(po<סpPssi^Gmkz^%6c}.wz]Wʟ[~}BrsSLGew;&n^u ;R\[x mrkmS58O_2_R߾m_ ɩ?4wNtP?rO礐KBfݏ1Vʺ #ﺮvQx}oGye&}FDDl_o#'~S#>^GWCwU=*Co'aD>7>O[|0;ӽy˷# 3x3cj TX_)L6^|W.ĕu+sZe:,z>gh6nmjf}7px+ٛS7p R9vi)t > :oSq_1 䫽X# _oW # ƚ"ToٲV \x[ [:!i+oV;"cz8ƺcO/:;1!x d%'y7uڛO5~3Ͱ|Od"}> kj{>m޿//o-'Pt":_޼XzY)AA~>_畎Q[ܺ_\Ďsgw<nrvv^~'/h3~/ʩrAW&̀e}HҔWIJC;؍ G[_'ߟ!h{ʫsYau / zq>,?YOB6gB)q>y*Rc쭼~ڈCOx.IO(HyK}^yUe!.U2}`x&,ً3g|UԞk;^?'cq<QiY)d͌GS@cU<c"^Q ^/ =^i~CNڏ̿dV|`~o W8f6:?V9]+:/x\4oA^K Y:2WM'}ꗎp~_u)uSEs= S¥7S~YE.kWL_A^Q/JZ*G2>5?j UNVx’n>^k? +D# [y]f_q5Wr XR^]ݦ|eO ~#⿦p+)[nrs5J?O=T?=&`=;gj9\{j`~º.hːT{\ >vV$OR;Yǭ6I)cPeN,*y:9u?_S32^fx 34s:xB|fvt;ӽ\qC(ۑE-.ҘRR;1C.GS:e_6E]_%_BE]I7ՁrѴ =n'wDO0v?jϢ}%+Rgh,ύ/?B;;]cǷ76p}sm,3>3֠|Eyz͞+ʐoQu[ָdOQx}֣1ۈU\̵|w WT(/Tд߿=>`2V\˖޾2Ao,>WKƓN/ ?U?DK_rA:;U<7?ai^F\V g /Rx¿(|/Qb! ,\.|2~?:jg owS)<85qSkP?ʌ.Gn|r< V?ͅ޳/s3?32fo1{6KNz瀷_5FiW/e,Ft 6z0|"%jB Ii^YQƵe\cʸ60k˸6k*+i:e]+Rֵ=+fM.SuWcgUpqiqCMyO Fw}oߚz}ز'>]w;w'2RX_-c2 skGh#yTkgoʁ恐=iI襂އ^;x>c]eo#wskKzȅ?J |o\3E>z7'idCcaN@yy6 0#J+>C˗/\3F'rT:Hx.>#.43O gr@Q 4; xX,puwcRϠsMGN+9>)q^/z K|$C8EjL*cF# 7=~mT _|T <*Ke]xW* ੄PT'̎/s3Ҹ8*0 㗬lϼkweƙ6g9g|'6nxcrmOeH{S&Էes`~5yKH= Gg$~Deۉ)0oNQ-^À9EGT>UV^o<L:w+ XcILOJ %Ǖ]ͩZ7;^2r;Ku 檃 =STq[dߩ5(4J}~OnN HbSR,$oC6ůY K4>/~ .ف/;k +3'1[U:)e }rM݇u_xI\{ r_J?zr-wWw[]ەre+Uu Aa®<W;,._IK>pXC.Vq. D{WW[ ?M(]`?*_<τKa~tTn%<^H>$)'6ʵKei;L8m+07zL1,{QlkNc/1+d^M/G y&B,৊r22J8n>냧ESe9zYt`~c?r;yŹ򘢺NƥI8Ww_%cc|xI/egȟVm)6;I..B`V=*_:tO2mOl{Fa72s-HHv<K%uknTkiݩ D>DtwQ>3=^ݨx9' V&)}n#losH+vBKc\y| j|yHAq]\w۸`ʺueW-nTFI1c%G:]ֿ5%mJu$vz[6#AGu|O}:{󺿇!cL˟@u?W" WO u4}>:yO/Ju*SHƷ2o 5vv/>^SNofJ3 /{X=dGc\eׄ&hfn@-[N"ώ幷}핃+F]䕽=[r^tx_E$pÿ敆'e[N )cG~a{v{H^9Qu}'Zߣ>)cJNj)nC}(ےxv_ؓy؞Mzχ'7q3qY?GNQmր~TByDGq<IgaH}a ű# y zk퓐(;1fqۓ]_VL"s+;~7N4CzdzA#;ҫ;on@V1(Œu{oP/Cϫ= .o CRn^^$׎Zgi=,,}?(LNveG'rpGK ^,]W?xO/4q]^em ׏/97`; z/2m_<mUaLOG6pK_jMj+Wa/3zAD|!F܉ siRy\~NUO)iXOr' $?0ExAa;ݎol<ЍEJ ]m*ŷ_pB_IWb@_WWy:a` Ekż:|5W>ocJ(_yMczSOieR/x /Q{G7Y#"}{]6;zH `~F(?CmW"zm9hº\r0q sE /lŋC|Po\xDt/5n;n˖d}+\eg>pIj[́YZw[BC%}Ӷbkڟ dcˡ'z:`rk׾/ч~MR: |+AFWFd㐷ڼ{ςAyͻ@}2*W^'ҫzI^qu:o3'g@[fOO#̯&̘?>'pf٘Wj!^qx^ďjl.%KԮun#`S 0M3U6LN|qg)Gq4_[WP[P׫U뫡p<_h~'tWz$eI[;Hcgd{'N{T Ǿp.+\[7h)Nݯ2:'j⮧W8p4qP/4qqSu>{YX r*-JghWF/3$]jf[xisދ 3M>^5J;a*{w ̉A/@zߩ)|N{ gis WU*e#t8%p+{)mwt~Y2?e 4|lhf_Cq$M8`^i.GJ9s]وp&`Ӌur/(a쮮|l(U}6S]{:;shy Qz2> ,*r]~*;+_YD_, ޼{?«@a{(v٬im ;]\ڼOu?=\77;͔2m![5NSO_fyC|;P OUx[>;1f_ cBWEZ0\P;ɝ|wѡ>!?DB\usi\ku%17EUg+Sq2Қ6HsVb,3w]ޗ _^]GJX_ދm$oYψ)Q87=7 u?x|k|{?(2_Ymn7g5GMSt8L=]mF+|y1V^Jx]O p}3*\H l)|P+ r8*zJ[0l^d[ ^w*ӏ{ g泍F+4۔/sw}.T?||& z;M^5k#b߳O?moo? j~o@iogw~j;rLZHZϔA|/ii$hݿRyEј9 p &>|_džVN;@$|23r{vz?(>˨/N]?ߍr s+?H\(gxGV`93ʫ-(>?Rc Ի?^C|ONN%~&9!>V8>Qۦ1-\y+] z_+4ЕwNfPh'xWt^'[/#(5^ڷ ƿzn;3Z;gNi;X[^Hl*o??*>"+3NC}}`x <K,?6@l-=e{ӊƁ"GƆ% ;U6K_>l0wJ8?T1)kYh<xz?K 7-h}zqJNIv,ڍD0>}7q0xbC-y iqOi0^F/t**\?[~ZZMy=AJggE{c ??UT}O-7&&d??u_Ҫж)yP/ z޸$%}uڂz8F/_鳸tmf 2_9838툓86KG1x5쾂4&^%{>~\szM^ޡWE(ocu}?Rz>H6^{i%̴15;kߙ~; /kf/0tLp\I4;M y' \?^oIێD˳c`-jFz- Xy£\ uq:><a^{߃;^~Av[amjC1Y~oo\!jCxߗŀ7> [~Kw`~v>re<cvWA4T:0J=#|mp|4-oj/8H iR׷G8\l4Us 8G%GU&0o3kh&HWwy9(w r]=x]'gv g9_lqƮa;wyԃ̡{z;qKǐK~_orՇZwy]}FH\6QEboǠԘVKa~"t; 1r|a[{g㊣zr_ӟ"n{㵔+&}{a 3\N~끱%Nɱn5ֵWQ#uTSvO3εǹ26εkf ̙Bf9.PIO\ۏgr6疾㱡 ]f| Oz~/zbN'OH?c#T/0* ]`ж@~z]!?mh#ܹ@:0޵bP(Z K~! >^0ѵ 5a xaqa暊+b 7$4[_O:AM)I50{£u5\w'D7] >k[ kR> O\rUh7{yIoһ\V9Jn=Ҿ~X̶I'+9$ߦ\]X]1)|$ނǐ9wWᴓ~ZJ pW*wm~6j \?]ſ(s?x-Hc8 nD& ǗA4??Fz&.t+Aۑ+7%?Bޑ@TyC \lݧ <sgT74ϸ$,E_c7&/}[aK_CuʞC&v#xH00ꓔi/78#yv+D-N|Ԙ Ǘ>ΘR '[;,y1X&yK< אNgjGkP{d^Ǔn̎g|Ŵ/8(m3+Vܻ@We+gAi$ڣ#gXfo?f.?}%GL{ާo)owP|%!%+k+.0"FZ~YxrijpYe#&R\!_I6(!`}odHzwnZ3qg^l+_wI:TX|]'G'ߟn&h$>'gq-;yg2dg+}u+i"ۮҪrywz|#Xng ?c3L 9SXnQmۏ:;@N Rx 6] tto&L[WĚT!.3\ܕI>|btu}߮5 扫mW?~'bg1әEmn!_DuZ+g_~o﷗; b^H~>/(Eƙt:K._) [SS+WVOOqNe>[vuVXy\~Rkog!_I~/'r&ȿ;2$JY^%$kD o]~`3H{E38`.H&.YY6Dž7 ~6'k(Xϖ\:rcǑ*1pG>C%l`F_wmgHȵȝ$l%^s/}O{W%3b;p.sLUg(m*~{}W|o;:x-MHOb ]u$^.v3E$usR@z72,i}Ox}GvzSċ8Oe+s9eyߠWQZݗri?dRQwQ ox"' {x[T{K ?ܠZ`H9GxD'n8EK7x32|#g{xfwZט\7{x5׵ u[?_TJ"[N !^/2|Uf#N_:P%ϯ]d)s|uF#k?/pV6Ź? t`[Bo-!Y=`S#<#zmJQ>!0 =sT૿kaN3C@ ny6e/=/In=g>/ql-1Hf _An)s^~u-5)|+YxI2i[sn=٤|W~5yZƐvgN~ TJvznUɫw3]/Y4\eXwS1,y1ҟ@ qL"xGj~ˌ6kcx'HW/ ?vnWY﬚񚂿Yw5݃~ĿS#gL_|Zqv|q|m#68UzBG[Un$vap!c٤cC>֧PnXs>ŘnGgfjWrl ȷ[~+mv:wMcB[A2:c.ޘ2Ʒpmꙣ"P}JWxwlo_~{A3B /̲, $w@HysF^R0p:{Nk%=@TqqK]r.uK)|, ۯur T ͉hOցוt]R?2,?3;Vg2'52Z9?& N$)9ZGvz{=W^vחxǀy0RgYmC~^Aʴiҙrnw_:6m{FzCHl~>.w~=z>gT)~_;_;NMu?aw<(Wo J瘯?Qy󓚷:خo;OyՁۂNIb4i;g xyИ+\ps wPXrgBx38SB>[G`iNCs:^2I}žy$ q+ ِ!s?j\ak׻V9մ1wQJ׼{5Z ʯVln2[rcg"S m<\׻P{?wrlSʕ{? vQm?~#% Ok;ܑ - ծ>V;խj#>Κг3 x$S;鍛_$1a x#n|`Q/1%j7_߈ =ֲ~6] gpW)?>2&/.ɿI?9*HOn.['!Xo.`)YcY(}Z7g:&rG\xgYEE|u{DV؁WE^N)Ws||e^vk7[\: BY/1%K$]Va=?)_[oq뱂ؾRzϕ׻6=o>7&pAcͫ5I/po؏(T.OGHsR;f;ykǚasdBo(<x%>kUv~?;ZƘ:?ug|,n7WfS%]9þǨ:o+|SV;õ98&O$X)2'Lklo? (ǢjcY }.}ρA#fpbG{q[ y>ҷٔѧ3ֿhμ-,nmuә%݊`>S9m~GdJ.pl׸,o* 9lzwA;X#~ L>L}3A <wيm We>|ٽMn'1_:)`=[R8 썱}?J2 ]<)8wr[]??~"e6'm m{n/a;)ۣL]f&>Y|^k~$^-.<Ś![\|i /Qxnq׏2o scܘ*_\xZ=cPm{ml^;/cjWy{,FLheU{zNmioF7n/1'֌mwxߤaMx3#]q ,ֿe#OD!_s*cL. 7z^c&-/3e7Sh+ֵc:yЗ>u9; 75~ęթo(=$(|OlK'߫l䣲F_ɨgcc2~M4(wM/! [$JQai^*+M|\וdunbN"ǿT&b ^4Q?l593T-gO#~m[Rt1r+Qb~%އ֚K|!߽;T<3h>RLoe)\~:Il*'+5|c_+Z.v+w_ƙm+&+xU;MN N#w >ʇ Wli YR3vd+{L|g-Sg^ӳwj͚ilP~uE|AdL8[zyqVAn3IO  aT9Ί \enYF{H+4T!>DHO5?7^; oқzŎb7?!e|o%}CߒnOs;5 w8m|'XO l8\w#:r9]M}I^rO4wEᗁ}Z+v}6Ȅsu#/k ׋\0;k;IכsX(o!~ /#j^` ^_`g?A^ ݓt#ER !'eyHk6˿Km"M~Jj(V?}{x/$U+{J?e%w/XGܤ=EB1f;A=;\Wcn˷y7tyfjs8?)O=VC[81c {{2W'"}lg,o ŮZ^x@xҷ$W{Tܯb _eO&)?Z.|AGGܽf=r9&)Njei3 k H:~Z$;poS}~75]a';"})<79odqT柂򒗌9Ƌ5/^ fH_AٛӢPfCg8hپ$sCs?G3W)X! O~˴łp!MR]?B+dlӏ||Fos駉1JnA58)T/yzG?6P'm{KKp)hyֵJ>MBcWn {tKLz9¢i3*DŽ{|COT*syn<kAp)ډTgg;dx KLmM֩{x;Ӥ_$SSe?_Wyn g&f ww> )} 8}1q|g/?jU>'xy,9an [^t?sYlw;m&k)I 1+F8?ܛ{bHgV?ˏҤt6|'8ˏB ˥{b'N]m}Ku!|$c1\ysr¢wή- elbǐ~'qtr&&3WE.Gz}oO}SAf?~KƱre:;YsQvVE.m_S'"|s%F>$A^Ϫr Sor6i= xs)ו`I[/@z[SI7e.58KsUڃT4suPz8]#6Jzr#3W̏y;^N@Xu;+5h?Mr3>|sv%,nQQGmsްp_?OJe__ᦊStTXw{yƦgHz{'wwn;Cn oҷBouףmbr8Uu>~Z:?Xw|!j;7xQOܔ|=ONmbu(s7]W[ns >&}~I}WXz͇} 7Ϋ;Ccl} \_GU))^,2.o_QMgOW3;>p/we|//^o0ḳ"[={ld?6W'ZmŤN.a/+% ;< osnow㺌;C4x?ﲒ (T~!^ߜ؅Kd߄*ݟLmJ /KsWF*?B{2zx&|dǰMwBٞ{fψ3]B?c9&DNo'~+?KoN w0KOB~#N"e5CC{x[3l̈́d̑nS?&EٌfJS$d{K|{rڃwm/'=ضz+%Py%ZřT!x1\e{MɀӐGKN:ݪ-iJߊ/KL {NJyO~!ٟ7gdr(ZY)FHՓIռG^I?;p>p榽腤o8s+{Ezlۙӥ˟فrv$ ^}ױ rAӗn륖]\l#rynO0E '^Yɧj+|U49vcP``΂Ο6/ x3I2e;f(C5_ϱ#t]clxjD4v__Qق~%ny9lZrDʏM!=cGxF +~{==Q٘$v;{>goǷ3S33lĴqg9 1g%+}e ]#5ofBOlu,;N^䖂77yMuk?:&;Q-Ƥ免.*}]}σ̋![l1wQ,}f{Gr-rwߤ ͷoSs[QD~_f7ɧCɕ;|'!nQq[|;i{c:1W)kȶ|_KN{?+ۦ>^2$7PY}}"ۦ~|Ƶa~?:6Jf9o<=w{bKB:@ҙ%Uq>4fmRrO;;߿? ^:~J[I/ߋ $΋"#6v};ORvA9^߹RI;Imy֍m] {u')7M.휕{n;Z\>sIyT$z 򯨲iHrC 7̳ /93.?Bc R_+I#[ҧXm!?ֳȋf'\{{ՏbSO0W 79By. )n'bO9$?ƕ:oWgӭ7z`\zYj"yH]"'gH빎2hr;/8V[L=e/TB>Q9jx?>G?c>ɽ8G|~ӯ/J/uCנـkN!n_C\}F_-FC~<#kQzn&]pն.:Dڗc \]S.d=ߨءr<7z?8SoƵd4]W.!T'$ϐv@#?Hc.9󢛳-Wx+0叩WKd CE2f0/kv7^ISc/_̫Y|ߕ4eP;y fl \>,EoѮyȱ']Cׇ%O_H'C n<)N1s;b6{BVB})UϊxOiKSBuk/z]x[?w<2/fUUWyxՐO\`=}M}i= \N|Iʲ@'Y?f2cL e1#].+Ky ЏCʓ?*{OjRKʸh;̘oٳE"]뇍 )_tr?FprW\?pu`/W3{qcyӍMKlp Ż=HD2qL387🇼AcCT;\q8!Pj7kBW< Ę{g3_f";EX zS&/R=oL.X* oE,77˧JR{P ~25!?+ ׎tlb c3w|L,2vUib%\#jKJftf^~Irv"rˁ%_Il}W Ǘz\{uy?yQ\Z>cGߓtn~LMY6Pcd\sk"A/xFzC[Ӈdg^BD>);7G_LrHG2Ӵ>oɌP2c $yWݨ?o[xbE3HsT>\-OΛ 'Une{e\ȿNOݗ)Ry?S #d[썥;;ɮ?An ulQnH:¼WvO!2ܠ2ow%oqlgBng"B)}/f5[;z'8~ʸ'ѫmMwn M77,̏e82 x[I"+R|+>Fvmܳ}{ )nw_v{<)c! Ÿw>=.?{OLalg7xT.3XMMHO:W9{;zrGBr9Cǎ`7Mq(Iω;Z Vb?P`3ؠL.+_jb\2q)#A̮lFy*۟<>DY$69 #)> /fWV?DDuK1p?B͗9"4W|rc)b~X ڵUq`N%Ea4_N |)f'u692q)r{u5[ϛ#'%{a>~1UWf)kcCsEힽ!Lb,ts>[n?O<zΓ|O9햦c%_H QThW~_sCN_5+OO?pAÀ/O *υ\[ v*| )A:c-C~7\XWFA~ʋC'Xgwk)q`I[ݭϡ{t ͥٿr/ {%7k^8|M-fz&6sPſ!څP~q/%,:?{pC.pҘ @).|g'Y/\;ns~fcya?(xWWLX`>w?nNWZ8ndӞ2#:=I:R-S#[VImli)!%*%4'jc 6w/y?bazq~ކ|ooܙj>;ps\ұx+L7"\q@D]Ipퟒ𭉎SGݗo>sB?p]lVy::s<ȯgnk#<Xu5uXnx#qNsd=?43~[h ďJr(º|3 d6O3:J>ߩdO'jsZߦt}tpnޟԢvs] U;LևIW_U,|9htOEIΏ7&9?))q*s~ߋ׿H1+RgT.B~*1at'?d W!(+u>J17o,m`Odצ'XZc}Wx*/=M`Bm9ږm1*9?O_1}pďτzﶉfp;5@kQ4Zk&kך5 ݕ26XɵF޹/}A?@;o^kԍW3;Kɸ)m#RV^ T30^P*cb`>S%C)\gv|b&8>̱'|.2;uڲoVMq=7^{s g2E|ㇻH~3] @y @>dK)3/@e:pP9r7Hw 1~LZ]56K࿫C@.$q@BmI*=eю|?σ#4!O ^GWV7?Țjq=JR7J ycŇi}i~cSϧ\]z~_-@`Zp>)ΒdNs>y~ ,Q•Az`r ܆n  ^RXAg^c9,6z_^g]rO5[g.*!%Omقuy!;S٩Ad3)ۗoW_j0 _F7X<(:#mlZv^kkI/z^zCJލi9KtK?~CwO/&\Q2|w(eykm|{g{'&Ɔa*xHf%Օdw`&u_Vo^?SwȷrrC6 vܯ%} X4d+^Nj1٦k47f>vlt1/HHf6SYzL(1gYx^BOW~[JY1'$C Wx N?w(j|?/DwZ?/^?M6Yi^&^\6KT2>Up{_+͑>?1W ƓRfiޫ^ 157s n6Zfrཀྵtrz&]HIQdMC斠#}Jϑb~r\R-Ӟ.G^ݏ N2O<>GG5{㵍|7}^y V,bt^O1c@nW6:-׀?{*@kX[)Zн+xoJ?-)Ɣ"#B/łu.h~t|hM/wn1Q|>VczB}\ݙ⟨grӊ[-] OK*HOrm.sh+9[SnL|gH0tDG 7I@>(ֺYrIA WY"`r Gb__ok-IV$}o8u#˷lnت1MI;9S1f\Ȏp wa_^FJ )4j`XEկɤxwkQО\<Yʰx*>H=<-r<c.Q=Nx~Vc׋|&`BsxEOas+aRf|N»Zϛ+*Pδ~Mbj(\3P! z үqrx yGTMp.=^˖ۘ s+ cC6ȂkK ;M.ǁH/Ag/\Gݾe|v$ߊ|W]5o|{}$?˒|E`>SQKa)?7hn鷫>2۳"/_o$c&E38ֶq<=ݡ2S=SFo_?qaK/ ]{^G@icoGjx.us0$ͅ$|p#קhCx7!:{7_wNddIOg%21t ^KCrgG˹Hi M+yo!m7kwQ?7!Yլo!N~k+;˼?Ȅփ]g2}nHvp{ucLf;Lj4w8/Q40FRwrLf+P*Swd^Tt~Qҙ/k{(rÝRھ?߹j/+Eo6JOM'}HOe_Wm^ هn|gg>ȳǛŭG!$ z}ޠ8o<{<ɾO嵎N oԈ i +t ϘVsK'Ow*? ޛ+]v~.R]/{}qN(O<{}SFo~~SطV#SO[}L^ZSy!!4޸U},xwYam24KcW'<8!V\7EeBcd1>חPzHH@fzz~=>K9T#}禩|>So/~؍~z|U9#}/n7ۭo|=k[/&'Kw~uU/ߡy ,oi6}8;Kogҕm\Uv2lY5k\?V _^|+xDu^CoσKچcI#Qqm&J OrEcjUHߦ|H;to1/-py]*l,T٦mHٻlsd168n1wSkt;Ǿ(ƔO*ۭ=;~ 釽53]__К[mgeo'})\yu"p"?3h~]W~gۢj9{:<;$hW) X_$ٷUY'7l`,ik۝ogMXitV86f>&q%{FVDE*E\ dUq) 7qUuMnt>tvU{`_S]?~ٟmQ4]?g(J0Na._KyWE!'wp,oUI~`I/#W^ w0J:yx\SZ~!\EO Ӊ_#{/;י?z^FH/@?DziUF+4o&em/|Vyb!:U~{=/۫i`Ycij5m=Ӓ}څfT&eOe|~cU{p=ږEƼ1>OɜվUhor5I_jBzme=*[y56f=(ywQ[Crcd;L"!gH';Ry aߐw-\۫~3il(Cu{՝mKu^ym\})~Sc+۔}C!~/  Ɍ>fuVZ^GN=/q{Dtw #q=e-!]! /=+!xB&6t\?ή;{?~%$#kBJ"+ YQ{' 3DdddfdeWF!B{y=9q~^s|9)!w.N%gb1n/ 4旲3^ƌzh{XoR2<}]o[f}Iw0ޟEޘiKl*Tm  *| kPMc={d'ͪp1`k yZ*e!g}?jut/= ۟bL?=J7AṞRh\ /⥻f7xzHpp$]= ۅWnfېͬ9D_x"ynTx90ϡ)NeIs_a?TqtHrnA#K_g2.\s#w e.ڣQ_gHf77{gV'v6o@ڪ?6g-o2W"%ޗϺ3[a(g;/wyuOvwdpU^9ٸv%$W>Km-㜷|v6{U%ds~Typ[IOM-Z^?Ix~g5'a=yȏ,C~J$Lk6όwk Snߕ칔0 +]֥eLg` 5NMQXđd,ߝ"/?Mrr9`^\""H*Wa9'g00W GKTFR,&o}۷V7 )w;Ļu[{2~<8i*Vi'XǤ0y; ~g(绀W\280#T޵ .o ώ6< Hz^\20xӻ3RwNI./P'Y ];qZn y<{Tpeg0CKXYk*o3S;zvpTXiT~Iw<ڃY~nF I䧇B'E쮡8˯*?'T5wupz׎q{<C󶯐}j/BI7tպ3bX^ }ysUGCN.Q+߂l7wԌNY,`?4n>~q휺jW-=v%m 917A.΀Fw %{#*%>O? T7Aމ+|Rx?%zYz4 y7&l11knv?rf{tnORlkVv]V2]a9 2;":!6soR:Πo.a%nj+{ǫ[gpq>yY"B+ s)xokeW8J+ԮG OobH_?+_oC>W7,rN&Xzҋuʂ7XCUI^ǯ)xS"#®nSw')\ tO'U˙_r=+!ԋEj YJ.!伾 TáО"GoJ?mC:W)ZB~> f//=īDy:gz_&t/Oğ ڵs jx:K>\S;gώ ߃ =kV[gB"< .ybۉ|$uOwtpo8*(]Wm]m '@ns` UP6'[kʦߗmdH<9n߃!AճGeLqؿ41p9G,#2z)g^Qƽy-R]v"x2Q1ͺ_>ezf>Sdpa5J'z)έ Tғ~ t'".+5<np__MLx&'JޏTJrAZˑwS<j^Z˓tY^xc}kzm~?i]Kn6j+bԹ) ^]-?F]{& m ˪M +^%QJi |<; MuM7N$d?1thbY{\븰<[מ7H_LXynH\Cz߯-m)ASͲGޛ^Ln{״] ^mV8!޻qu')Ҽ.p9)Hakvs?f[6j3czA>ЋPeH_IzL]u[^UM$\ ply4=kWFq?( M[EZ+ŘUE7"tj ycA C$!Wg/-?w$l̓Oʐp뉃erO߃߫r=4ض*ЛKQ^z _:x3T^s{+wvcw,2FxmtyOUcN+ڵ@o ޳9S%gqcK"j'iP*oqq9x_'p IKox+"ar~|W[oJݸ{fw/aH =Μ^~~joңIi:6{u(|U&c|<ۼv. ||5#ۮ)IAoWSρE8_WVxd1Pu 'Ofm''=罵$\CJ(Βk|MkB~f'y}|NvV/x>^Ec<>C<pyyC6ՀLf|MW 7.<+ J7ihxwnsbf*k|Я;⽦~ Ÿ|R1-zeԽ.8R&xc˫ *Lӹs4^v04qe60RzsR^KC4 0?P#6B_:a/ ,ܲKXMa^0w]["ii)H׋ goCKY߱CWǣ\Xր׋x `P^m5f`Wɟ'#Ca@č25 ~vu/;->0DZy^1y-uZBƋCHޯ듇'ys^ G`fp{u&$sBu1ʾ|J =&|bЙ0|fAPI~T4)^8ľҁ7 w; t'ߏ x3T;e?Zjާ^{%< /y {cھ}_o(T?C4X }EOl@oSfRBo^{x|Y ֫MADZM uޱn/_CoO"z]z?xy&g{ZOyk'8Wok*sx(`ȸ$eηlnRGsMx"nzUzM&B?3xG՘UJiۄ\.GeD^QPzrfAq}G;sy蝺^Tz5wFk %yio 3S;{Qk?g _=oE)9>zއ]]6jllxޅ3]Q-N~q.D"k _'#j7F^G$=2Nކ}ʂ.CI_;OKlfL!3Vzn 4jsdHaj[4!?7Z=>_xI]o`=SC!'_3:Ҹa{r>J]?ƑSg3ॎwet3!d#;oM 6ե]== t.gu)3W *^bT^J'[A݀wz՘08ȳ*[YAݓK k$_.] D|<7ptaMo+>?xW! <#ePΕ{*24nya~*.=Ε#~I;gr]Q8Ïta,p9S *b!@6~a-]4E܍ _}SQ&s.2Z<xD=C=i{˓;=s7jVNTWgoƜLLq~ ?_B2qwra?.*"to: GyRM½@Y%~X:ʔ fA:qgtRڮe/e^JJs5^+wMV: )[[^U߹^ o,̷iBfK~*˚/f9'i~'o}-}] |NqWϷsop;LX2(Fq _ wj;֬P~W·ڹ6&e{_ºԡ|1YצG_./7Tڻ2?.z]_맅 w!4Ǩ.+8IcA3q|*G[QÒO$1kSDn<Ę  ;euswI6v /ƈf}= p;3// |moB֓pGҿY{GcH7m'\N'g-;|N.Q]74| W9A~۝Zes uq%jyzK=OVg+k| m:/Jf{h3?hrO@?"cť-{w.+-<\/;ޓCvstuu,'vK{*372پ$[+x'D^sg劊A _ 1|,,5F<^U#g^ _7QrNk>g kȘ5TX9.W+M2Bnmj)0ӡguLX%j7 ̱sw@ %3\Nk@zKMOG5p-u^} E dfI)O/$./R *0UkIf^xf5@iLyD ls\jd0*S6MD*=zIo ox$a.^ #O/E'M:I\8krakwP/ׇ+iqK$-}Y_$ׁW&=߃? we2/ pcL7%myCwx@lox:Q āu{]Bo!K .g`˾;M[{3$~k!D]y}8]aCYm`>36ML Cu]_dB{?1P}\8g+Ox=G!KN q3:h>I߶3=onJϟ޿'<Q注{,%xKI=]8*iVRX3&? [t;+zvo1}E7Nx4Fg/)NT_lj8:NEi8)Oc9 p~hs7;(Ng ˄p mhlw{ȣD2xyOd~.\sep^+7}]y_HeG法eq ed~"?.ŇT1f)䇉x><[ܘ1զ{}dۗ#r.IBz oߺ%Mo܂ɒv>$yw,cHX9+R=Qh.y&]TvB,]tX^rSQ]?ˊxbw xWUO5TaOE_S Y>7ft hSoJg]4)X}bvN$yJ*\ۡ#gK<03Q_ ~tsj3[>|e%K)|lg*TOUeDžC`?Y&e(S!O)܉cQ2;쮃< k(60f Qϫ+,aq˩2I̊殮/Gm{n*c0?[LCm"O?GS(]9SϹ5^x 1﫛AakCcC4 \dOgq*$mb{^ ^1䇩/ϩ^Uq}dPq6M@^6Sx{1]sR'^ emKlk%1 ?\ޮ E I<4؍Mv+sˆu, 1k]M$>7>WǹrYnKf}cEͱ;sgߐtoTs| CveT ^;,g!} ^S.پF 3C[?oa7S`h-Z^Knx/5L@Ǩ>in v^6i^<J^AG@މ(BmWFe!.Tk^,+s`?N]%*-C[+?!nmx&럯C'5N@߇=Oޮ`[:7t*-g苟T?ǝpsHuñƃ7t <%v7'?LQ',wnSwn吾Nwoz nxǀy]mWD d#͐zg"ͻ'>s#Cs̡mre/SYu_{*y^973Ns WTؽ !_䌸=g^Gʈv:|gKIjȽas?6A\sk3O#;~oW?niYiV)^k9y=xkTw&C-kof]<-FoyҤn-H2Y7jVo|c6 ߮䛨5і㾐K/3a1“G|2xyq|6gh<œo/Gsf~ұ4҉?r_f?+}f/{==]NSG-iS;}$vo>$b}ФpE{+gSWny.#`#pӝ6wדx+v]I6ϓ2 7Tč]Oq#xW254/;ϵ3歏}J<牜󢓄$׉ms .җV~3z^Tzs>%%"2'@n;_|^c:bh|+;~?Ho3ev%V@\'.*0LE&?S[z|hOL\q_=R~V[K7VզS t?sc2'_9:Ow@g#KsPؐavs۲^kGR޴|Frwos3+io &;.5ޥyM[w}k.Sƻ¹j|xo87Q`vA>?_=;ݿ-l&7G~ir0,"7tb&=mLo`qw7lofZ+[gPx3UuZZ["SNdzdx'ߎ6iLTy pVru\E H+mpA*X%z!_J)`9\\~i߯t&Y맩 i{UiBnnV~d6Rug_7/DS aLx2Zn7<."<Ϸm |{[(t0mBwBMK0Y7ɍ{Izz{g{^V޿0AcT^6}Pkv8/+۰sۈo"lgeZFۻ&] 7J_R{EK/.JjFa$|A1vO;#.=K3`?9w_'(Ic,B-:@\z?G&kI~p($ž+$.9fu՝\ҟ '<Y|"]xuI8@xh1c;'!٭E⿮pWώdh}h1/P[|aB75+ﴷ6c!K6ǺYi 7M)] B~} W7y䣰g}ѷ 0gtQ篎뷔#gtȝo=hx tFd {g%OT /馆7pYClGblxPӞ/qv%p:Wc'4DfQIDnv*"HXƒ$-܊5& ٗF%-\r˖݋<>=W>{sl_W|]x|&6Kd u?Ū8j|G4qZA xzιݲRUXWné+Cz3θ0?cИr+#ޯɷzZ>~mŘq4<{WSWM䉤:=o7(d=kH~6N޵t^gZԵ[8S>KR̡|^8R'߆`:`}^ʴ?qSkz|I_ \o&:Vw#<浇QXɠV͞ L0{)kִV@>S{y=o+^íN_D]~7ns|&ל~UXҔlĽgTh. ^%*IGU{Pa{:\|?EC+xTQXEӟ~(# ^MT3++?RwSwW"7}&{k{k_3oi|K2c?ߓo%?כx )˾yݕe|奩h} 7I'ڃDz{Ҽ|Ӆ@O`He)X(M7+R*޷CaL[|KW i`U/ʌۡ{?P$sHgro&z~ _VeB0_v:篍`wexl*>yQkLno77֣MKtn0e`w{pĘ|om0[*LSg5pjCyu]pИ|^ttq=3 NZ;)2ߪUϩEgY|_e!Mu)s^{zo<V' Hos pv.fȚFwreE>G6L[9z`BP!L}3l? rik'=TArʹ#~vWyrwmkw.u3`~)R^䖲{x_qQC!?]}޶ iO$d;t̵mOӡׯH޷Y1 }#݂&8C?;gfovרզN5EbI9s\;?'ȳ/hK?M;g>"v kɱtWJs9x&"'+yIOem-l}(Q M Ga>{vwDe#hw C{='s-RtI޵IҭM? ~wDsۊMt}J:6a-:oA~qƹ· wZmʼn6 ~nIr|Lq8/Ϲ\|9;_Ey%fqN9Ǧ dgx=Uy5.S#S-7sQŽC1<-)|"i<޳%^9'oQX =cxA?w}@滶=RX϶yk {\=-r5 ;ʇ o^CׁQv\S^$ >n.¯/p|O?+yK^OwtzyJπsyV[W㑖i9D_%zFhpwT%/}h0>s # G@?E>i$\^gϫb;[K[4K /}"?ys:Q<|6XI ZK!o$elk?qe(ǵ 7zt=Gu^ڗΛz?wxr\}'.qE߆ՋW ?w;(W ORxKdIx6Sg81!?(yݿ%zscnɥV{{R{*k> QJ;yx+K]yMT8{(C^(٥+'vW8٥>!#I!)ȋ){iJ~,?9tkA^#pמ)+J>D\$|/PR~3_3zP/޵ЋB=w+zLO!RJXfy.S^ڪ<җʯWGg%3!Kl`}N\~Fn%enqB^8Iun_}kL_c̋]|/3,>dMa&n IV66@D^yK: \N Si[?~KSVٹ {IDuZT?wfVE{;핿uK^h9=; N\憳&yc,!߮!/*\~7+ǏRƚfܼ'y#`o0x7z˩ 笼tm7O1~Q✋So^}'GyStKgeWzUHTq;rme£Wx!^!ʼe1x#zy2o?ܯ2c/<~_o<^%EKzc2.^»bpWwD!ע/kWj(p*x^u֩τDtǁ?ty+ɂ-o(O!FX}i;4o[|'k{ĵKWǬ#f^/7v5]kۋ_?o7;uJ3$[ |N "J.=K|!ɿUeq3.B{r u㉜3t]r!V.Vk˦lx+'7! 'B;o}Y\9'|bYd,sy, zw|w;z7g~X\^:7wt)J]ryd_s]=7=aL.a=y|1 yH}{wV|h#w~lQ xt|h>8g.})cKuϥu@v](|k+R?&Bw鼔)ճp3_+% I~A.ˁ%f-NHs/aٙ \ .?+7rr٫hl2>y_9`كQ+O쟵Y;wzagu̯yS7RX|Y8ځׂxi !=߁~&(|Zᄏr9.d#~{ov ON*HzRoO'|7c+uyr ~yK]X_ڢx=WjKKmY%>`ݽ _ SI2H%-à }5觑~~鋤V@>kK+S!\ ;8e/?ه4d;'z$P"[ EnWzSx/{-gK\%%B٪LlAM&|ny?^0 ˇz/xmz]$x]>jPz$/~*mϋ #UEu[yF 3ܖ7#́?/Vݘaf˟eK=k:lϚ?#`M |.pO![w0jQ`; O\۳ q._B7*oәsI8T\mYw{yqL<4Us}F2Exckew>k`_vy`Ϝ8X$z`U=&JZ"'rY'e9t.$Sy-%>Q#(mRQ{>/fs %]{^;[]cQ8t1*𻋖+>ZBB1qrbD:lR Om6xqg3>Vu||kӌq5),8kLa$o/oogexOa37GR{mV7Vu+[#Ƌ܉x|}n.R"y̗Domxg䕅 t%?Sf:ۯd` wDWvw}}OpiP6px?*q^?jƍ]ρ³eۭWo*{n-$̯`|7<ܐ87&pͮ>Xڙ]7K'Koзm5_gO#N[jx L~yB>9 _#8Eaksdܾ";,X4/ c 0h 4oi~1߭Hm$ɟQ] E۟gg6aV®39HDEJoZDN9㍕+9bQNrvJ]==y}k]s533)[Ϲ;rSl19m=pEH֝g#gXzg*%@URlTq9:Œe yh )yg-2Ȕ)mb[O )ݜHJ |WMO,GrT}/W?%Q~Ӝ'](ͬў?< }J7#_Lcr.Db˷k^]Fn#{WS]Уsh~_ :R߂3QVΟk5wbJ7]oy=ڐ\k JoR-z:˄+]h~j [3yNܨA%˻=;:~ i|* N/ L3iݿNݽtI3ciff8鼷z. ^^4g#9p~3bwiBA^׫H7oB1K@O2 W/6^ڐG]{5S8co][{y,OQW{'ުD}/u촁WHo4)jP`]@ Z-ɑ<2 $?ɒmZ第H t찰,.³'+O'm?૳QYuC |BkXǯH.>+:)gc9cqz׀Og =H_yZ{}FCߏsE7Ù>^S1+fY}qâBnFRwsR}!J9XԏaӋ},?q} ?YV=h|']3nr:A}Nr<9\(}ap~-Jn{:ƽ Z:{9")UNLքK= U?6߫!Iہ_Kt'OE"\ p2Lm3n8%~ԿCOh4y'@~>?ޫnXam'[Oy/6~/5(g#l=rȝHZ}'RY{cKc ?sIKVo|o^T{Oj^:ZYHuX)ݮwSCs廞ԸC)јc2eh 1􀝺 Ag{|ޣU;Aw t$cw)#&@;swh)-`y^  aȽ %'[8'H !IrZq&r9$WQ꠱уfn=4}д,z !u5'5%&G Cy_t|3,.s1dktGNIh|E>A])x(i:G |_haM@s +a.(k&QxQ 4}t6#ǒ{ V+9/?B*ai2gu-W^k __?lWtW&æ-ݴ16ׇ7h;3M\] 4v} ѩ}tJfU맩[e6ϩ8?TțMʘ.w!dkNRhlǰ85"ϼk47UE$\My[yqr7 LY>o*Rv3}?Usȵ$@Otar}:n=C2}-z%?ŢXz6Ts{L}mI1I6ˢh2<5x}T|{,==8 uun+/9A; `v~R?|*:y^{7d`kWkB?Iϗ?ey(ӂly&ro(mE7l̀G@W?e h~^>^\nSVoy,͗ w3re^utqܙp0:p7WX)w׳ Rp\%D !owW[mWヲmXj:kE?R5_4ScNs9ྸ^K!MDrImYo ^ **5MNl1ʕg̅fͯ窫~TMA-ݻ3KwY,W8nc7gloi8{9sryq'%GDS?2.TEY3/X ~3m޺8JC7pzYMJqzhKZQw SyyN{j _9ς5=̾ [O foNWgo}t=R_rם8wykn8rwEs'z. ƁM,2YMY9auCP;6\WKޮD掺uBjf_ٳ#rWIM0)6{_ ߞ_N~Ӡi x)ui- %%c+b_fY Y[vr%XO_"\w1g$p8K)?Ky>6K_:NEߧ'r._WK=Xwƭo/"l;ay8r%ss_N\/v([6xrBU_,7ϣQ{8 \:p\߷=ONniw큫n/5+^5 ܠS>.zmFO9RoNuS!$LvoFvRFR!%i^ x)?/Bi~O\K'V;|++A:ȝ\;ȍ(ekw9;ғ'Am9" Ce zvNU>ֆ\{,ަw-KI>Kv4yz:~__uYRz^uǀߙ riFslњ`#~.dFwKts*Ap;n5~8|r9qS{v|uGB^ C5vag_2p-.82tFF:ݧ[/ {ݏҳ?,lGN~i]ݭ){MqO 5w&.p@O r4O#سf~;?ٙ/;>dY12]Q_ޏTIg.=¢E|ԺT e%pIg?U6 ĪQdS:@"'~eOs5v$sk~/E{{{ok[ i ='YUhR12i؞ kS1t3AHLWs\}~!f+u(w*J}cya:; 7/?k':gFsvwn+>\Àvg~/3FmzN?oy~qy/hN'OkRI*ʻpvϴ%F`EgBF ;a" epR'vjܡdY!g%R?~> -俇'?Kn~]袡+~eLm~ubY3sE}32J$=;E2C"_~bm}1Ӏ;GMb?bB2D^NضYOeZvq|d_ŀ2ejsnT(5KTcn8u*勘%-oǂ2~\ 8.1"s_\ _'ָFB~.1^lP9whsZ߲δ]u7/Co;vEv,)E2 A?bU) ̺6_d[eYt|fmb_]\̿G;/5o::\|?f+3ƴIO%1mWOoAЏ[~fq \D)FǼꕨl'܇Wns=J$:L{eRK?u&UK?{=/"r5qX/a/Vl~mD0+3tˮ;{AD8c \}9;I&./#mY t>R5oLOZ-Crns{Ⱥsz\ 9K>vݝ p}]lךpeu,yNwW7zMF]|i]HvSY7}S=!>ѿlT!Llxڱ.]:{bo%x:Y}Ϯs'[9A'OOH'uqx a?! N_׃pۮ>(>~as~Q8Sb[}ewp)&*W%[9cA6e~:9qΝGd'7iǂ8(셔= xy΃z%Èh^[cQ|$:=3J== ~V986,Z=y"gѧz?7O#Λ{ N΢>"*mU>aTk-IwXs t;Of+[KS!7P#v0sYy~¬zٴ ^ gi~&Pt|-"0Nb\#8LKj?ԋ/#[Mc,ۈdiKzYׄ&4 .q㐖n-Pel)GCٕ\u<=f,cƌ z%Z,-Hz))hCS›%!^-RxI %K")P4ss\T?y{{|g_ۄ9ڐ dM*oc?M?5*ώ6zdϩOx/صl౛~&쳽fڀx]Ϭ3ϭzOL7xEx_xI RFJͅ|'m$Vݯyq|' ?tOF=^H>&yN.xߕ2!D;knw~c2y ~ 9?\I,qk%nN/gݝinz^7~č^{]@ČmkΘTĵW\v㱴ͅ=jMjpKޑTg*˨=dwf *rTE!UGNP|נJo"B!MS}&S:dh ^B^Ώm|GkJ%~HߍgۗtI:;osoI=CsMYQO'su)Bk$o|ZBNƺv7oubw'6,xz ]E⨰ݜoi!r4(W<Ԍdn}7 ansV{Wu+?sJ+n?8z>};Q%j |a?%g6phNe`7Ncծ9ݒ~W9IGwj[{{W'ga~؛`_#DP7 nx~/uK+:4F?Js?hPn ;U9kSl^^ΎTuӵL%~o)NĻC ǂ78Kmo17 $_\u9L,G GE5q=c=xhb!gR.j?N KK~ܙb'zۼM_g?Hooɍ]gR9l"367쎁7)/6OHMBI\%t|4p% sTX~~BכRy\ֵ K_IFfGuLTYJQ =\DfR6xsx|F(f lmC\V>=Laz^w(xUgJz&C_23ڟbS[$ۤL?RZUj>6[/Iįd,cQz^d׾DxئqepGo&[&Gfǘ_pjiI'}O^l}J<?(k_H7 ~R oN|p.x&4&\ |.W]Xn8˹,.^mϧyW)n&yyk؛ֈ4K6Ns[27;-BTMvƼڐ]e*4S:9W_yG 5 ovމo}gz ;/ Jvİ|䇼|=hxkr~wv_8.9c)B~#Zߦؚ%'| N||ީ?K~^?I MR#IO0=Mn|J4z<59Asdӵsћ~e$F^2W{RcT+[//y6I.蕯=[VB?C. inWSЯw2i@/.PX")>?ȶZB^קzs}vXww1WlQ_]%0:;KrB/~_.w$F3bϡ"//Bcy3g4tN1oFtޥor o'+3;&(RCmpV\.~NOlv\Mίg"~ U7_ >Cm {O" `B7Ӝl9. PT1mI~9pO[A{U$8sQ u\2gnlk L66qn)DsHߒMi|G{-RڹEoivv8;'*P7O2־g<=I`<=Iw:RߘE߫nh>y;9"+/k(dz=]/yOQ1}],O2,@v*si<yw|q~Jr0]J;PIg+쥅܏>ޣr8 !_g'ow/%Mt?'Fk#]ݨV޵KԚÝG\aw/59}3>~s[؁}J-#nH L\ȽxO~H8M$4N`S^BԾԞK F">Z/IǗ/Rv231?j= <ו>kN/ND ^.0/B;d<ݑ g:_> <-W#sæc̀]o:$2$_ {^~E.׀x`v>րNPʠwS{ߐMɲ5d묬34 _畫]m[OnYn.L0J U_>psi~v`ZdJitcFwz0[q>TxK3}W_ I5np+o]l#'X"};. QB|"dQ!Ӊox?7\QekwIwlۧS8_;Gar_83e *ewg8@BOz!/ƃd7KM")U,k˅<M v?YS?K ހ*.Vx>pV3VqK>/A_Jv)\R|ù.KrO~W]Iu9;:}2&]`ߋTq]~^*I>WCj~ty?vC^=?-C9v|NEZy-<;7|WJ.j!:߻Ŝ1LOr{'WטNs{W@:?~]jkC-`|oƶ^5u$qsΣڳ޸Rֺa4'=M7T7tQ#g9p+{)/Fe3-DͧXi\ L.櫍ӓێ=muݿ)w<i[[A O3JUv<{EH|d0'9{s| rŽ<==lLs׫>~BRiZyfWءi+aNv#^/}y*Խޘ"A$g+-^n67N>OC9,U^?L-ʷZ$7:iGSȷ{/++bt+E! ;x亹.K[mbRaV%/'akEwa;/ bOow)3H l!x%g$:eiOO;G 11acH K?J4]bQ{ ;[k8'͢䧈T7x%}tJz_yx.Zԍγܵ-9O Px% Wsb+M 1n=48i}I?>K]ZRfo`,~~Y] ʙhK}z+U b6 U߾ޙ};Qݾ{Y*-~ie: \Y|y@zeySs7[< y>Ts zu.?v_!yѾ|Wsi/xoJ3*J|zrR`A("Ea'cqi2^3Uf V(K bYൈQoYLvk~˂gbE_~?só.oEbkѐT?]A:3 2)炁/Gz8 ۔.YKsu+놓2wL6~jϿ>/]ʉRV76&|)xT> 3"k\].o7n,p2(.e#Ǿ ^Nͅ+'Z8\}EΔ駝}89r~Nsu{vȶ\sPyarbS^\EeOg";w?cqZl ( "Jw(.") %!% *(H+R"|_A0"s;y&ܹcdO ^i=G R|g> g0*hb3|U!a%erqXdFqWMqwNjTJ \I|@چ;ÓZ (o3cS&x<.P|po[Gf%PПg~ /FyjKIO5SֹX׿gBI{lݣdQ:G:x \a(7[K[ /&;;a`Y;/^9_%; {J9;UKt t+y[-wƷ_N"?; U e't0/b<]% ?JGH[t(z6#|VC J#NYGM&՛Hy"xȷ$>wBo!zsD}<ok:y;Hm'^_؟j_ki+Mt*#ru9)ߢ}'AaI|gI$|̫~9).ޔ?d֕<ƛ]3 #ϝ|W|wN=~kXN0;QmEwkK+P?h<_Kg\{"~O Px~5 Z~ ϻwɛg /]I5]ORSȾ6}װw 8lGm޺Rxd\9ˋJ4ywվZCx5&~݀'G.7wu/croHyxq륌-?K:qrnLW_ӞHs'rN|y4/pu;wI7CY$~({RXץl yIY) npEy>Y_🼋pfBy13fW-A$LқBP{opٺ~?F! v 'A&bN K)@Q]=b*Wzto|(goxNGq2Ì˭9 ~̿S^@1[Ձq8|W7Q|>{,.쫥_dk+MiQv+Z![ 0UƜnԅin'kCʌy(, ^U|S^ xU;~jLxg e1z]UFI[^w^H趹ǼtK[ ^Ol_%i>*a_$6y|~ P-{U|Uqq\ŕ*u ȇ|mWW|(I$;x%Tu8RKZ 7Kk@ʛCf'~wϹ?2~ׄދ3_7B:ۀSiJOU]U5$_HZ!՜ \p1-NUxT5w9?’zL~_ZM k_ruHM1c[ ? K){%{%D{NDװe$ϩpA=S!闶#䃼Xcsե- {+6,7I޻kÑMGN Yojz'vznHd 7pQ:_4]o|p?s'+;:=c0g?go+5ŸDާ\/Oύ6KkE2ӉssUـuRݽ/CKZ <_T~KS%^>0#d9Sa^ְ^񼟟nvu'(t='>u=홅-;CN>t,ry:z?O/#z`^|*8Xd B:k <e.yP?_'ʁx y~Cz4/GtŤ2Eky?۶:Η˘H8.DN¥:G'o6hlP>~#)nu] gOuqr9M<նWXvNxdc98K),\εx)ܺ2v׳r[&s<_{ R + *ogSJg@Jk)7~_3BZY/>*ڞC+S?hu8g.4),Pm>o}ɋa༙}ra9k\,6|?:śi:MN}kcOgi`7#Q!񯜊 [LzݚܼmHYlĵ]M\=6{os/p]7u,pwϤmoo|;RG)KjN mA?w|}7`_sj?ieNm&{6 KW\y-;V\SxR3E\}Ks*|YJ${o3]ǺuWcVtV}~[Sat;X}L#A#LqΚWnB b/o1vVsOO꣼eޗzDr{qS.C|YG3ګ M^ o)ʷ;A NwQi,}0A07^rcw~UcKbcwV=bh|8S׀1-~ %zgޟ^l r߹\.^dZs.˝ƭ.2{'0|V,snK^.2z+ěS:R%He4vV+/y/O'+낔Q̯[I/ Y U>7w.e=fۗ3ځeys3,?~ Ǵmt[`~>kۀ4cڳ8Mrql]U]W%GQAJw$/lw||!s!ׅxgPXys_KfWN\*yiǺXa2佼>v<ݖ*x}V-|COş,^kUp{m_k 2O*Cs,\$FÞ;J튱v}'ϻW?A ޓ^{h?8ڰ}7MpsoM?v%s~J[:ZO*q|!SLJIFOT>>R51[)ЛI~y΄4 <}QiӕkЛQΏ}>|HEY?[o|;T&xe=̥iv2 ; yqJW;qpxۻrezc Hq'Ɲ07Df*W!Yy<[0Wa{սoƤ\wM1/OV a %ˠ5+H޳v1 ͉tuQi61ħCzoc e*yle>~!ǓY7ԁWDo,9v\$7BCC7^8 ߥ.ƒt='[Jwc_ϲ*..%JЮQ藺1xMH:ҶGWDpL-y+ԑWV3͍)@^-/y!'@m>kxziR`:^B#^[IkL}| ?ܷr{-p<: G|*țǂm_a̓Hg<[Ӻ:0|^kƕ-~ƽ~//||*ɖjx|{^D :>P9qN7i z]t0w՟ɧOYs2WIͩOzj+Ke焼j[&#_[3v킟@V<{m(AiK'TF-c/%epU,2t]xD@o*k٭O[tt1^˞^_oi^e\S~l'%x=ֱ}'}+k]7) 'S/˛BܽNa"cg^4~pԅ 2ڤDsң.hej׎? n[t}Sz%> _@u\[+V3zKP}H-vc~GyQYxI'˿l-я׼/kؑRI;g4^cuHov_܍ݍq@<P`2߾O5v>֪Tu!mxIxB+ImiӰٻ!C7/ɛg8O6B\9jz4~^OzKwA;oUx67wze7h{ Utڑ𚁷x OQx8V׿PMՓ; /_樜!VYe2Xvz!ݮ,K>j{M*y{+z,y_aEĽ+lC2zq<>xk}nS Zy_ _ڟG/B4¥kD_9xMb>~X {?zc;)sCIw "ϟ`WC?R-E{ܛ845y?͍ҫRS!vku].z<^]4>qFA0d")2K1Ukcl/d^cKx8QNBl4&ߐg,~6|d1ҏ'Ny}Oo6׫S%]{ K'XVk2̻aM gIU6ϑxY&:3 璧!#2E"ύy8#]τPnk灯l6D/8u~~G΁4Uҩ6ťאַgpK |:CC|ڮj^b[Hw$z$gsLן)vNɒAICy/TG H^X 58/o]vΟ{Ы S<ye|׬A^U#^ߋt-cP$F^ G")o +;;-6ǭ:tB:߹OHwG)~}mpg`܉Cw^`>}o~))wYo}r {M=JsKZf~K kuͽzpx-H#)G^kEagFe4{so}෪vU`֫cr/S^Eۯ~G.?`|$yFL~g0x,8ܾ'ˡ/~&c v~ywryĨpC!a7SWWy{By+:]"ݕ*[UVOe?ǿoT Tקq'[[ukps,;hRi o\ m ޳^'B>Ƙu mp><\yw}=xTS<T%皿B.:mx]0̍F)8u}F]ߥSׯI##zzu )LE]7O2~Iރ~/_ykguQ9N.mpcC7;ꅹ젰x_xm|LQ=ӢB&?pJGk`o-33ueze41&cmRw9:kQwyݍ QyuHj<π<Yk%>;*Y;O v%^且dN4?%5|GI9\VX;C=&^EGC{y.ۤJaŷ6 vm7f WWq4WھX^sM)ݓO>C8IT8+\KosM(ӿʛP\~6/6+瓼7w׼TݣowƐiA>B7o4׾caW'~m_K)~w#j cN d';ӐoJCVFlD'Ҏ$.*^Z?܀nւ͋ ^3Rں)Ѧ/Euǣl_^?$/3... =;o3,^uI(k!S8gq| ~`1-*KN e|>*ǫ–' ,4J3 !.o'0Q_R 6EAdl_M7H|cB#$^7vY~.Ucÿ1p`9C*Ƅg)zLc`<Ð<_ O` [! ?ՎQOI/; ޯ> C~W3}fd\8ixlbm|՘<7 Pxˁ S٠4^>MOҿ-^LIo&cDh;^l>~t`~Oy^ʟ&b*Ŗ7?lDa mo jR^(3vy烟Hũ|/;a5]rwgM/R`g/,g9b򨧒s)_A(%~LRˋo$=~Z;ocY=|NDt籈|9$THܾ{wE )>x_/?S˷}3NC[%]ӂ>FՕȓׄwC5 7 ~i.䳜92*pĤG}߆^yM^센"O(ӂBWwFMMszѦű 5J Ic3$yJ73ӷp$u40 0t.V׿uud8}7{]ugX3Rnz7s.zg^ʛC9_kj ݧ~1l*vĘp2PW^" $ߣ0~ ֪g|i]ywf6Qw^eXڱM&!OVG2$/y/Ir_x}<y#+p*>87B b̯Tp Dz qgܸBZuz!|6|݅X{>ڄXGG׳f6 #Ts#'^Ja+ˣLay#phEzx>{\K:q:ӪjhXcTL7f0Yf1&{V;7X_uxI;o'-$Ymy)F9uաk˾C i%yj'PL70n~4YTܛix zӞ1WgUdyoŚh|3 xIpv¶/fki@\x8n1kUv4}.v(}v°vp\tU9rs7"뱇Vg̿񯨸v80?|59T좰(+<#򺐯RktԮyupm^[`ޣ$ߖ֕%~]9l8v^ewLC෨o8կǁYnu(~N;~_=K,o 0ϕwmw}>e(97=8\yL%>'~>\GziEQP򯽾Z 7dBerTFѽw܅w;'{+h#k̄)]$ /Y3=kq'Fgsgp=* i1VW;v-gIX)[[ْs{w^!"wg?6.vo?Gylޟdopծʳp]|K4n7?V62xWKȋQoVڭ7F~qi~&ŦQI~ZPJy˃)H{9Y^ǫw)T#`iL3ǭWǙ"|wkُK@Oi=/| ۋ*1&`mL+j[Kwt~`GKl\oSUc\6O׍k|BO=[os|1jWY7W'#2)ǑlhoPU3HxSxnفS?[N}үO ?zWn "QSą]\l> o=DϚKsTrkTݴ-[GSk,׭V*tρ!Fe3Ho,'Y\#gJ ZyS;ysvӮۉ|ρf0vv3*$=@{#Ӂ$P' Q(0?x^agxLwn.+x6_δ}ѢgC0}`c:Mw\O K};Nz(=y>tOz1? io3ye]ewGKC=O$Lݲ^Oߥ!' hecBNӠ7>)z$_ ^BRtߓys#x9/-诲qq Yg|p#pRR,V>]Yj@z-46A %:z}u@~OMwt7M~|/g+<ߖsHރ6 zJy!z%ʙrEbjn;PxrO/Hp ۈW`oُ}_ӓ@~QaT/~2Pa1-{t_h=K䪩3ٶIn/x;kV֢C_cʹx,3.6h 7$}fTSX!טx8 fFy-k7vןƺk,/)~|e Up:lccؘfnwσ%^!(+|opґg|^᳐b=.V ]|ļu,7vQXdM/1FW2F/b]36*~A`ǞRWϹވ\\WZRl=Kdw!-CهS.=?7>Knig=F9f`H > r}<ȺO#ުnϲzbn ncL=q)I' w6ߌϰrozf{Ko|'ݒe!72>XCǵ2wy zn˞.LAʓwA~T9B^/Wlwq<=Gj]%Hc{6oc3Q|93=zI5.)Be?wF2]4e`>s Z{ܸP#pc7wū== {v->'q؇S~O<3[^a]pݫGsęwumm;׵鹹ښ>׵/S̿.n;S~=, b$s>#e ~=M~-y#ZF fh[dyQǩ~(j>d=qJmȭTjXصӚ@"lhz>l#=m湶c9}C~JwxGEOaBH3 ƅQ 6_c4[ *=}=U㾒OvGdGrr&⦇Hy U;ıtsKo\=#1ʿuS;J׋lǽ/(;܌.ȝPrM}rMRzSjJ&#S)- 9o{M oCgO_^[E~HlJ{r{kKg.~6Me ZMm H6rdg ߸~l9~g0>=E׀\+K!?_p}:Rx 󜿂ߟ"~^//AJeWZj ]ty"qێ߱v0W¢v<ňGGA =#fʟ |7e]2D}=` ? ~> r%síHq,}q4ޗvgя|y/'sf _D.?wS?)9uKVEL^UmCi^uu s3$ Wq޼໦ǃW0Igi P_ :"˿ǢXI;Ӕ{z7!]oi(9bVƞk +kl7:d?J}InLcSS]*bٕO;p:g~o"c{H) ̿PX~OOh`P 6⽪aS {bk}IϢ4L7M Ŀd󮰸zӻL0IO)&nMܟ:$ș<=ދ2d[)<=.3p1 Pc~և,'mQ#+Fc^rX?[.@1bNuW.[d*w[w_ yi Q=u޾1d~Ke ҩok0N/m!Lm k?v`6ҞcƆgQsWn^ _ ^u.S+S[oBѶ2_$=+s`|Wv6|c 1d^@ΤP^Qe 7~«,/d|2Z{ꀗdZ+mU|;Z_iU 3D~ğ/iV4}M%$ˎ!Q6hJwd~V3x_$R},g\&+b*׋kml\v+ fu}cK*?P~.xf_m}0R7o=oDr{ҧQ s $'B+|AE 7W+5>s{NC-c›){[)~hK6MoZd' B!}ogXCޛ8! ~GȈcxA~-tS|ni:4GntsĻ OPx{0ߓgrΉsr?|bHe~e=7gP:yG}R]GVn3_͠=UN_zzvzHИ^iY)XNn xOĊÑ\1o Շ۱p>qiGl-qo>)G9}/D2I&SlS'/R眛m2Wj[nv8Ֆ}Y]K)rfkȭݪg?1"'?Xw Ukw\լl߳EJW8\toҟopbg3UwW3`~Ϣ]{šdvo׾Q )Vw R6ϣ%soǝ[%n&ou)2Eq6]c 4'O%qsXor^[y>㱒,x|V 6moBٞ'KYw wmޮ?g,`ewԫ攍)lv+;W*3'YEGYkQm|t H7"REBP@E@S:)P)%EA@DZ@n~~s{vٝgs-wsRwQ[ʦw#tO#n9>m#lվ~±8N~>I|[^Q~d}N˘T1.j~R yzvs<{tT4984i*ԭrcg+oAdYyG2 quNW3k R҇o|zMt;z$qyYTi<sM݆sh0^N2g'Ƕ S,~rEFys`OQW"+YB;oHͱuϨo )ސϪ:CJTZw VՎGטA]]_gEb1M}}UHmh/e\I7R0|)mtg#q@]SϷ[ _kTڳ݇d~ FK[eH&oax WSFiNԵV}BVF?lxQ9s\}`i#) o/kr}xg^œ`w\O'oHp/W{oߥ/NhJfu)^ؓ҇e fkSTZi'fx39P;Pw ^weba nV7Vmk8o-1dPBPt^cb̈́6b]V[TayY>W Og9S'y#L>6(mn=_"9́ľ8=WR.+/ۍ=\>\DÿQ wMwxL5Ȝb2;_ҝY&3^Na9ZMkK,_} m݋9~루XՑ@ЁlE2Uoi<הwJ 䚶R[1?PK{7'y)G; }ᛏ]Xb.RYw#(^Kh#IO5Ceppk;\F.zjp l~۴HY`DZNa-ZgWY]CƝ&|;}1s7͔ ;CזÃ"~/8mK]Uhn:Ր䱏_&n.`'%.`'y{"6t>b O#]}yqqcAa587W[uY@e~OyL,QS[rt_U.WB]~b 9]>%I6ތ*\h_䥮`lvg/MU\e*vJOS~^R>;}9Tŋ5Wh?2>^΁䙹=i{ll=__\sl1!keih_9eزn8mmW}%Ur9z&>\6إhWvU 0׀ =]ex{G~7F~wz[>?DYkԿ<]Ux11Gw"_VV׸U y~ uő;_@59ƛԿ&{]C/u~1ur'8Zki(us|5d |>T:vswW)!B]O:۴ x;7z9ރF]_د$nv,X/(~\~6&VVq.xϖшc3dgQ!isp Ѐ-r}klEɱB˧\Z)չ's2~@݌xڋvNY|vqeo!c?l*veTq/_=yuՄ&}wx|vy*H݂xt[+Gguk[ácmﶏ/îG8U4޿z*zdט9JտJ'#cZԭQ]bO61ʏ˞,YTs6]R1{~?LXlٷVq<mQTm uGYkKN0psFrZߕ88GrYF.7N{D%#Ίxg+^x*m?'t"|Uŭ .|jVIݩ? ۱\ΏNC*w}͜k/Ϗu9y_Q־>373e"2`QKⴿ zm iwGȚW3[sk;>; ]M+bNU&e.eÿgxD YgoEt+Ŕp\.HL{nj}B';~`)'Qf H?^VS>8:뵗hG2< Sx{sm17wMtupruNg6 k{ V;t2t.m۳M}6iAӽhW yvJWk_J6vnѾ S2\tV\vO|T}읏B VRűI?ΟWq'949Ʌxh38f㖽"wD {uw2cqوvᣝbߚ>2҇QW~6N4=niמ8ƚ}8_2N:˥G~;.)aӂaۓ?lh^gA~>h[ο6rj#uHEvʚc -۴[ T(scJsnXEnW,K~Q޲ _x'AYyΕǏ\#˽S\mdX)߸ ,s{pi/c꣡_x5T T\bvo]I\q\{k:<>Ozɛl' ʼn>%K|hR]cSIo\ ^ }OYۊOG.F+Fg P]qmƣ'IIdq2yY5MF 69[lqؔ.'tͮ~HO99o~Xw(B1nn ~O}_=wy~D ^GڳPWFڷW泊fW*h+iu%{͍7ӮBſYi7v}B*_ =d{c|ПOvE:+.?uU+88l^Zۗ}?6> j_u5U=w]ھpi;"u7߼Mn:]?eHg!pmuÕ{nl;@}SFhKFꚫ|PqȾ/ɏCԽRҭ=և8JUuwdCڷ}MWo|t,T;Xʆ_߆WGCT|(^4ژ.C|:N]璡}[v꺨:& 0gqnոWn1=l[v9/8vU]BB ګ5/|6莐eإ[C\E$ n0_?entɲY],nJnnK.8hmWo-#;Lr&X7K_Q?<g>qý Qwn*|e!zb]o} ҦfR]ڽ-w旓SAwqrΣץf6[}t]G/uZeҕ,CN񻮟G'(wn!D=XiP6'GyQ~w`{ޣc@V=Q<^h'ixB#5sySBƁS8#n({k n5}N}WWowRܟ,ylmp=%-4Hv>;|~*NQ~ R1AKycOD}Q밸xi]+iwT~*s'_|I{Pw7ȺO`߇zNgOiC:"]/md=}7?6ko9Tj_tP4?oKs|@I=AU؎!K_@3[>{Tn5߄"KrIqD3mś/46/^PR9[6הRPzGC?\.,G~qH]ed& s,{ ^ d1W`[ٵ7iSqv^^X2@wB7\V2VƆue][aoH/ E]qΒ͔04%K{wZ34‹D׋t*y;3B&{d^s͹8݋7Y~E:1:_<O3%+gX;S;2+R\ՙ mϾև1[#|⭊m5]ǰL;9+ YgY{2WLyй?Mg<;|I`X~T(k$p:.m9zͦ}0rf@~G/b`_23|(!˺X\""r~sIFIdy,;c:Jf<=Hߌ_ .^" 8R_RlS}߲tn1&K eBv}qvNք> jm|J {u훴$W0XصO*}u>MrHw6;Hٗ|o>S'V5%{,m}> <꾉G'߳Q7'1HڸMԯוtg$t⿯u7cF_eߤrSB36/Z]k?]XxԕYI]?W]应2O!=;vvSU\zYrO=㲳ϟ~_JZ֟cio=n{=;tbouv~Aw>{’x'w}rAu:L܍#Ǖ#uUzB3W\nLoLrCsɚS7>ݶ B]_~5}||{ ý/2u= {"K? PnJ=Y#}z19.UZqMsQ\ˢ גv nO׋DOP׵WccƣIPt_tOC Skc"|÷m+a?BԥCۙG3wx'Du :͑2|hU|/0lKRt*p\c]g.Z֮ ~v ˭;mr=RF1OBrv}aËc5+M'7_OcSڗ>DߠR{!ek몭[RW6?-]yح'y-6tyN~ʾTl>kJ]/as }n]Kރ>cg^VcPWG!mmں҆zпA.!ۈGNc9lbt:'sC3p2!KH}#w\c1:}GC|mYvvM}}o"՗kGyUSL$l}Jo8F1Х&{XznB[,.[6#/YFy!rL*}iעeq%|O+k>YC/~y48/*<YS{UۼcE>>} ѧ>BH~T~E&ukͥs6Wv|Pe+SWi7(wmw*:y5ʍjs\E\wxlHF4lsdkUBOw5e oOKOtRW3#I3|y^[  ?t\c9 VN$e3߾ ü]>ȴԷOLsaZCW-k sC.޹\⹹\i}ù#Կm Ne]O.g^ndT&ĺ5`fXOu7"s St1y\#7x۞mggrM~!ÂXA_'LCC/%suS8*H[6{;%_mݖg3t+5N)&:S;KQytoEۼQC,p4+2vPPnN[}wu}N<onvڣ}췐UztټKBN򼻊; uZl>ߩ_0Wz>6e>\\KBJ|uցʴWW7g;B`p/&BfoUmD2|t"w'6mvO~ >^}ݰٓDžb(cu`?th]j0NKd?9{\w]Mݟn:t9d\d#wIWvд4m٫e&~_߹a Fcl[iL2ی֏ܣKֽ{ ~py259 >GHJPʣ,bȟHA'}S%m/ Y.BS܊Rʃx:kFaԱGC$-O3yO/@ ;OwwK~'I\#3ҧ(NU1 K'OKّπI\ 2/I=)e28*<:%Mp/mkwKd;A&G,m8jw%>9 ȉWSSS18?9Ip[rF`rf@YùYyʺx["?^C~;8c9ñR. OȕGUi.LI#FNnM~5E)#rc#T 72" Kn!yHnAn En#Hn^G ;/;V4 \Dq'r/'>~yRA]! *9B.N O @ ~<sσ3/7%_!_|Cʂ|K\#8QMȁ)hppr$x9<|| Sp pfr*pQrp]rZkA wəKɲ&JΊs36K nDK.~\\H\|\prEp3Uρ&I 5\\\<:ߟ<)#e+V xmUΐ;; w_ w\Sʈ܍<_<<|<p%ɣ cCɓ7Oߕ{R_"Ϝ{) ;Fxzmؽ}ғ~_,&=ҽyק &u>KGWWIWK$#֐c8;y{+?J'oU~w_4+8ߋ|np;^0~ AWdW$@uPF|n_'%ȧoH%G FG,uS\.=7p+i{oocAG4@~[~1@_#' X.9 t{'.A nD I~)97{r^r> rpҗs k `ţɱ%_'if8\5#WH~\+6.8Мc8=ypQrCp.#7!w/K~w-_6*5pSr[pr{)/;rgpW+οB~<x xy8a ÃyC#|s*<` uN!˽yd_I'7B3o/owQ^jXvpN"sdydK3dty|Yo4Qk!n;d[{}[Ddz盚-?3lBqwS"/mbIyKΝVFw >2H+2GZ4#_gs%CfYU]&axou˅OHz%<$_tn%us~ 7{caWT ?b{>>&'uܫ neor\>䱩C9TlS 5u s:0+i*K3<|hRVcž7%0<|h_[ B}/Om [uh=dxRW|,-iE{"Oۅ$CpK].سJbo@o-Zl|كGSާ:+\_,y3TY32Aӷ&'g_ʵߏZ>tIvig۹Ew-)Ƅ7xZ0mka[n-27''Df^| >RD=L>粊_$Om`} -C3yơ*}Sȳc佉޾8yRن ·?EՍ2Rږw^u:vʁM׆2U#x⥊7vܷ7<\J.4{H\.^b)"u[_|2ecx-#}"} _ xo!Mg_xیaqLj[Wkў'2%&S<짫,&6U緒*I5eK>Np{y q2;uTi,U?&g\ޥ[J'Ե6>+75toLoP]~v?mϿ\fKwT1&?~?˩cOyc. 䅘]$o L%d& .Ng{&дkB- !7G[|a"ם[ k:?Cަ\qyY[9&u#`:7V16Iq͇@o ?o`Qq ݻ'䮻\sq<n"N& 31$ĺiSQ~ oOt8uQAߦFv4|ӒT\^qr_NTwo}4}6˲Y}h7}7[K9;ʠ!/P7>JuװL~29!NW=/''&m?&\mߔL;|s{oh'h.GߑU~ K9<d1Gߵ[lx?Ng7vƼ0oc]̶wKgo}޾.QCoηW{{~/SWw)>5Υ/Q}Cw{Ѕuqq%?Q狪]\]o4o(u~::X^ߥ_Vú)g2|Mu YsՋ1^*xyW'G`,E=Rv FM=;F] '9ݜj7uB Y!t}h`xn1< O?`x>/ F7(vܵ Z(:8nb$͕ l~۹+GG{}ލxBk6I4E*} NoG. V}7[5$ ߡ%V~BQɓCd^l}_|/^Yyl\}[OO`:o;`׭s1W/uhNͻzKdY'JDi*Y{4s(3(Bhٽ9̞8ޠn:t)NOw %{ N =}]?XuvVk}bNY#HݕwK]NO ]O~OS+nxi)^xwAquW_TŋUl]?I]{h{uw'2 pu@hwDV咗MUq,J#l{l-Bl%qk2wpuWW tuzW6X3|/|#sZ/nΏT筿en8Еm O)1֊(̫iWý9z/iEҷv'}sTn۟X;oKڝ_ܶ~# ? uIrʪy(]<]'>#YA&Y!OA/7J`;N]\ϫʦwT!.yotCS*n9]瀡wo٩KT1;߃6sC]YD sq'4WiTC>ѕ.M$ s!Exa>գm=J:c<\yJij"A0Rw\]Sx*MȟGS{)}Wnm wy|K wutiaF A,;wsxڐ4#sہ1UtcM\ qz[Ӯ<܆P/ /"9 MM_X=xTXp-h-ov5CIMKAgZo!/=ax & 4m]+_{^3Էztҙ|8o+ӦGü Чinf#tEF{xOϩD}gGY[B]vmSۅӋЏنlw6PK!#t:wɑQ˵o_cɷ_1U7F9(GUW|P7=7:uH| d_a'1|?RZ6?(?*VT'_yF;y e!e®O=}?Jm4cZ(?oM/)𙾾N|vv`;v}ۈ9m/b?~59S2xVύ'Mwrn]٩ 1V}M9-]tO.y˞26+uB>9|S !o3u!\OZT7F*v^[f!{[9oOK}nJ:KNyFN>quo&oHdn'eēj)doSdnWd]}e9^g߼qı3u}7pMU]n޸ai5|O-{Sw'g]46qfýJ/;R:o38oJjz3 R:v)+8{3ա> 6\Z?X|'T /j OT叄gG3ϊf_N]v%ٿTp/{&q=ƒnPҗO3Ny#188}7rXJ>Yv6e*\RhZjA&L]fd\>Kmty6,X[~M 3lݔKI x ϡV|)l<^O\Gmx]'ɆJ}ѕӑ{>?']O.UK_g|'ϨW=Ag௉'"ﲾ>xH•OCDij-٧%Gɏ c,}eo?ՓCZl"ܽMI۩+?^w,*a˩+~e֔u~뎯󒗐 *"HP%J@Y(.RAAzo!$$K*M&;0]{33gδ{ν^xhh2^ 7ᵬ<@}p<Ӑ,gd|8xuBt9R ˀz6J5]^G/N3:wpww8mLm-ٻe8N2Gy4)c,#]rX*鍎^V7 |2"S7~ψv۰-M:zO<|LWx py{)l1L^GƤDZ4&ߨg&̦:  s-؞޴7Zw }Ϯ#7y汮N7v n!~~iTg_o<\WiSīt{|˽i6Fszğ|;)>Gywٶ1{g{ ^;)؎eW/pW.*¬hcLI/-Lݖ_Iy&NJ28;eY? ]Dy?XIa*++c?Ưǀw9VxX4x%'S͍_ ~?rH.iws~~f%3J9MnΞm!;Imo@|`P{/s%'-x;)^%M4藺7So =Oƃ)ҿHk址6tVGAgb> ;L~IRuHZR?;leܾ`*jmW7O+A$hE6-K/N?xKA-g'3ozxH~D&:_UA9\cn+_/ Oecj]͆'{wRp{ U7wӣ;cyYvm*x8s/N;>,>OCOϚDnPﵴp>"/gJ&26TkBpCy2'(̿pORNcҫٺ܊SI =#=gymcg}vzGg]¯efۡ|ON?DGyx = 5gi5]a'$}}(0})rM=:84@|3ߡ0‘poS8Qa@:#}~8fC'鹠t|_~(ZVI\>-|BQvʊm7{@M-ķCEGy˽)74=~*,I ~' <; k;z߼Pd0.~0?p'亪#s vuĻ\Z `vW;|$4RX_݊i,>in=yC ny(5A9G{9Gs:Կ}+m?S&]Eن3?SzPZ=kcڏұ7յ^ 0O%&>Qe:_q#x~!eIko->3~[,c;n}f#9ov+An'rF* OUrӥviX~{sN ʙ?>_={ q>g D?ǥPS'c>ǟ~'?SWv]Oٮ-jy淿|z! oya1xcJpm 7 gV Z!žj~߼Zv空лw|,`ܽ<tF~IfjGREjK/s/{+wg[Fi4W[GR]pQ^yOmFm?ܗK%H2`^mU:x}waXJ uQ3XΟtݾFQ  0//R7wn/y|9==|߄?P-XlzǸɘ8ߋqsj-_}c\^y~,+,I'zCOgMy}ýWc=gD^#zF@NONnJe*_닝D,ALm%SKV\|>FV ws#R_oQ—[p +CpSp])ƒJ O%_ Ϥz; ϸd$w7\oɄ,w}DNKQ{O[^S7>rWS[p]|+\ܗ/F*wmD7j]zз2'|vN#V_PNg"3H/͓o7غߜ qu$_Jx-s#^sZ}.$x'e{rŕYסw<]Zoyt} *)zr1ƐUOYJf7釶_#wO_ScR-5|H-_ {kg!Mz|0O:;tMu> ~ *SߋWsV6kkLyGR\Rwy\4oNsuR>-M`7徑 O|~k۸4Ks~v_}!׎tU:n9]]kl5h٩5^8_Ljm峚ך|mN7 ߍK0i*] $n ʛn{b W.{4;'\&=SO\=^C]}UXl8Q{_u:8/K~9>)@og+kSxItnd|pmE%߻* p7pA`yXƳT'G 7Xf4 p廖ƈ\wCiQ| (o2]~jdQtp??֍q%׺Duk⿿ֵQiJvZWW(=aޱUl gi wQ. bEs>2kS9y xΥuΖ׹~c:7vY{'|rAcg˾k^Jl>v2O/_4ϸ-ϗN p=CZ6}uz;W}g}';(3X' si{'py5KO&gLr?3>}[=~]>OV>v%!Ym{gk(94dIry|ؓnt1v:g],VJEr]mȺ|­2Y0= q[{YZw@|7iCp]2n{S=,d1&_*9G\=oIJԦmlڎԵI^7 ׁ2n@}cmc?9|-$Sy_E9k &w.(wdGsh-<]Ow)80"59a|QPgB6YgC=!V== =r99S'ٹmfP:c\|d>DOouMsI5h=ǟ ϣ*'Xߺ#ċ}oK;z=b~ߣNa߫h-i4UdؕX1c B*"{4CG j25})mDEuq|^熼k 9Y>ꐇϞpL^)3[J%ۜn`O.7g#3Eyu~Կgy[Br+E[6̀Lc|&9`>ϰq;~1>Wqyb!wx=}gF#]=>o-VAg1z_s\ ex={QwoTi1,񯚸W|N&6ic!-~& M{٩kgo.^HǑυsW=R]!'> `z[tNX Gh~(+_@ <dp2ʴzмvߞy'äq.4 ")LgCTȇޠL6/Ts|?,pM̴m}_am^/?7xx2]X2޳rzcUUXUsR\S׋ǤWUmcap1.D }΄d |B l}`!4{,i>ܙПtYLo{$_)SlQPіpu9xIk Àsgդޞ]M rfuk {W+CiRi Ϥuc=hK$𻖯'm[=7:mҽޝqxM!AN\/ wB殷NN6q_y|ܥCX'm׍`'ݰR?Lo ,ccC&NTWƁ3ywEp ͼ[\\~ \g9T)|SN$*L1evh9ߺ?B=C͡ kg<ix)WX iYktaanh 'eKqTw*"\"xS #qؔSw籝$/ Sn4F*"D< kTcm>ѼaN~&Wo7i iOmNO7q8oh'<݊u`F>N8FnEMp{Jo\DoI'U-Npۜt.DlFsܳ!>sY^͟N,|s%99\!չl< |Mޙ O#~jg'> I7du6_un xl~SvG R+&q )Hr MıH.[(xqXut]8r7^ ۟MyS,Av>;׮~t<^*TK5eD}TPnҟ yVPRkjp+~szҷNLqL'w vyؓ9v^Sw-#;3Jrg[^.k1Sٵ[ -Y;c~fn[O , ۾OƻA0,ˢt%xN_mVz=u{9xy<uT_zS\+ze + /Gq[v .Z`+w˞(?D-!W-!A;1{3 k9sc@q7BKCE x^.q#i<*>+ ^}ۖ[v[*Sz_,xN*yf֜F}xxHg"03Ӏ;}C}޵z>+E}T`u~pRGZ+ةg|o%4я6+AT!o鴍#={wNzXq9ǫ*kG_I|?63C6~r"7a/>εFzmu^K8 h+I+ $.^gQVQܦ&!5Bl|vqԭ^>^N鯿<6a0]!ܔq!۠V ޑ_$o2a0;KGWso2?3hդUӶxQsdDZWO+QF-?׎ #{Ei赌2'}sJz?!KCGl{0.i' wXu2q}ں[X^3ų.%D:6ُʏNoGkmk2zoK xa 6qDGcSbogWy*UjL"kc/鹷ƅ{)#g@8C}ף3vy_=!a}gl~\{w>${81]ꤛJ[5a<>*&mFMª-m½>!;&ڸD S_h~mƋ¶^ ߟ=@^:: )Or g ^|ԇ/KgD+3<K>So]|+7S|g]e]{˺Uje<~PPx B5``~/u"0><XIsֿ2ymց!ť>{KxS9GU_)bh OTz<9Qc[RϷ& ;o =oq0+_~GӮi T6]=o-GtyR{ZBۑC }Hw-8FjXw+8s^Uh&[SN ׸/?i5X6O޿N6z>=s跁@"$x%a/Z| (B3U/M |^K\򗑩Q!mBp  8I@VMK6MP,ƁA'x ;Y]+ֿsW^xk?:9(/ |U+_ƴ&(pfq/p8Y읚O>j7]D-nmA/ V[:I2y?'EXWE:J;<̏y6<qגou>wK`=2^gga>c\~#;@.JQ>?Q73JûÜ /W}Q(w^R(^WW0Ӌ2[B^9=~h7ѿ6xX%yjv7_ଉ F yG. 7 >,τ3K7gi+®w&r tsG@lsGkTt{X{,h foz,jW5^4F'寳oCl[Jj_&χ%ɼ4IJnktVw;ҁ[O Cߔ_/]tU֞&7AHBh"IA,&R &H U)ʢ#@PTP@S7̾cV|o{fΙ3{h3R>ܜkle%Quw9/K/i;E|Y?Y?>]'@-E~?RTa}㩽9ǀz2( T/]LC?I5(<;m)Lz9،i^)ϖC#3_R)1:&=*Jee`=Y}{fQlQK,vÝnq`JGVw`ϖQ_K{IQN<\aQZƗ߹2< ~燆jcK@g2zG!~Lo0l:ar:#`w`g8ٙewqmgM5L^xqɄ;=Wl֘q4: ;:x'xM!D=>Ѷm>IsmXѶƋ#}qAP?7 7^[ψ:{Y:FTck&}@|~z-aܟ>o%mݏۺuiCQC QopWyV@HQYL|Ĝ}>sⱾ9ɜ3vR;8]o ?d_.Sl~x%}oo J30hj2)\>Xẉq*8|k  QlLyV⾛C:|L{$_cW"M9pKz;r|7O |U`|6;YwO\y~x5Cx.k˰Slp=eYQ{3B&~|{t=π`xW>{t) ONFS$?k}͕X_L[e~8/~ɥygH?/ppJH2p=4ke =EwdT=3Ueo`_qGQ./JM* ]&~-3Y_w3eu7@~7u;)ÐA(ݗEnP\޶wy= plvWCf2hN/|>ۆ: :0g'~8"ƕ;Y%)Ci`~m{{ܟw0? | ͽ-_d-DtƋ9^gs+i^oWMxk9M+Ai|F˯@ԑ|$hy3' `* 6ml <8s-]`[2[j_Üώ^^]#>}S ֮e)d}V}Y;=ym#.MokfWݵM?덭h]tz=Ӄ]/菀s zyDf|6'Ð!nvl|f> ^Ǘ'`{>|h;f,0׻, aόo$bYlI@I)/YKqqx |Iw$-ϯ(Se"H^ Cfeȧ|P~coLvχϪli3Jͭ.t)^RsI^^7(`H/iY}}BvYgoyc7L8u[/ɫ)?)Lp5x˜TL|OǍW9읤ȸj'n'}5o- #a)NZ7+s>k?xaw6m<;yx!λz&9M>塸R!?7r>_;g?s3ZOp_ZOu-1ϕ. \eRu-*6y..ZD%7p {qPjPJFww︼|~ ss'-!7Ԡ1~#8+df}]Wr?P7E7{"hT9e ~ZB~Shwxr>[v/O76|O43 m > {/(Ac;(˙/S c_սͨ h痿}x^/.bz}[ $s(TJc }N-/U Fe[Tr5 .%ʝr/wCcv. )k>zq@4^R9 3Ol]>kKϭRdXL>㠟g_ 4sQ~{m 9}*T!/L8BӹwP8̻נ_inekbI/W,NX%\_Qz?Ж{_ʉ˴Qk_%u?La[ˉ6vk S'(MKjUz ţ z)94GBꤻ j)g1 =%W"&MC?E{Ia_o{__sj}ŵ:qo$&F-p\\N5y y-!gϏH:!c>&ۀ_W-9Ʀ~~uS(d?Ы'3m3_(?7&^c?M=ƀE=>{Ƕ7c.ǟo$"Xu&NU`L)OCs^έ섢LP Xikʽ. ^3[9[WgCH3H _SGv0v8S:8v i$F`cSM }?4x[?L"JjK1?0/xڹi?, OGɀ7fqw]fXv;\a=6ֆɛ|r]ֵkfins|/oN|}C2G#>'3}]nYMߛl_/į&h-oۉ\W)σE 2Mn=?TԧI> E򓐿_Emxޥw_)͛j \)_)/!ښOri Ӆ# z} ؏qC?rFNyDjH?}K!sMk ƟN;)LÛЛ'+Қ 5|-OGL yO{(DFk2j3v؏8uJq- r}s>L'^_W:c1&k&Z kրtw9l mg`==/C+3C@a*xۈנLpZ];}rK 35Q$Cnj|]ip5ʧJ=z)Azu6ʞKs/vo-)ujJepF~s @ ca|jtFFer:5š$=޷N3mw\)ݱfqi[NjB{33dgXtZ#sTkQ ?&1c xgB=_kYz?ױg{j]p!h=~ϩrM|>xAu7>?Iz1ک.3{=ۀwͩ3APa\߇Mg 2z M_|Ԝol^?Ȏ<`u<*MVJ ,bb cN˽hBkxeopGz7m,jFR&Fv.44ŒJvcs*|x JbR4/Qp[tg'~%g,&gazo(ϛ6W7͠D6:Oͷ)^Ǿw{A:[+&}{ME9g] ~Sn EQ#fK;mdAH՜Һ&_oOG1~֢ymF;FkxԿ#X"*hFsOwA^Q_6q}(Jmy^90x/x v/a4?Y/mhaK ǖ9ɼ'e6{Wl+uJ.L~j&'6U"_x^mg(#6Rp5izGE[=T+[)]_}Z+zz+|z){︣Lm> ^#jhdmRX2CpLgכo>6JEےjĮT[G8O_"B/)|lo n'}>Co \Hs~o}m%fs4TC;)k礉CHO9?*\SؐB>_::||z+ w3w~W;9IkH%ysu!h`3Y`_!׍lQNyh]!OnK9~v!pbE ,(3."pq;Ȓ7[:`z75[z ?cbD@_g|2fy_7x=as9y fvzj@,o(o cWEʤb)Gra_8lWU"~TkYJM o\DZ[Tƒ~-xo,٢3#SwB] f?GWTL΁_i:)k҂ۯ\]| .Ͷ4[_~븅jG>+H [VEQc:/(x9s[A)Odd`Ll LXJ9{S{yx|Am抴C]4 wښ!<Zaw!Pqˆ|1c*[\L`YF"Q<*6RgD^m&,Vjr>-odpߙ6\~ U*"`{ ‘'"tM?/^J'ޗߗ\@a/4_ئ|s'--N6 _O9#_s}gWcϳ"K,e,%aL3: Ì!1B,P(-gZ!I-)$-${޼ux]snp2ߓ RMbmgՇH *QAoUNH1vNgm2 dL!sv7].6"򎿼V4{[~dnǔ@n7N+7JOmzuK ~ؤ>ޏ6vÓIRSQ}׋ܱ#{s?dg̤B#M+ _^uyroJ%i^$uy!onGzlț|PR侭颊#yS|g8kIf[NO {&o'{ڐ ytӦϪN!H*8T37Kަ:tKu-9a)M ϰ-RroVzh~::ysROBމӀRy^r[t1p!Jzƫr/㜖|w/Υ19Q*?V]> oׄ^f\*g<;9rs;ڠ ,'lg.녧G/|?]ED5'{vRX%yG:rя|op3g6ќ8aRLYmsBsG?ϰl.us-xϡ:NkI:fbL$;7LAHɽ?߃7%>oOv8$@͟C^rysk)׿T;`00Ltq*rM.-mOBvK;I<C++֦1Hfշ]po%wzH>t*SCyMzpý&?mbkxN`#hSӟmjLfWoWh-[U*w&43>5 QR-7]Cp~[lOʈKyOmIei* lܩoFbwxop>u Fuov_@,o|%ϐkclFsZ*)UUL+GτEo+X%\[/?N2'x1Ռ!R_ 8眎Q;(B^ o{Ja[gF [r<-5TQ;&xeqfxctIg ;5X29" ־Z/>kjq^ʞ8Δq]|?2zN:7׺x6.V<٭|4\R2daq:o˴` wzK1޺_Dw{ͧo!/oqfYފfCu_Zvg^SzQT4vtM1[Zcaܧpd&iЗiqz=p9Y x핰R^37v9᥃9^si/ۈׯ6F{ߧ,بsӶGΣ"mI~>G^ OE(|d{<gs/_s`&(H6ϿB2L_҉^V}&dӑ}%߃יx׆.㿫ule;m?-6nt.R8\^S7 &G6ob|]_J}q?v&2F;}xiC76FljZƖzso<6mރ7FVf<’Jʾ)䝏7x,Y~n^35x$ Xvӱ͛IN+{u;-Q߃s'[iZ~ EgH{|"񒵍?A7.ef/J"je~AۀO{fشǙNtaHC6j6&v:|논qk㏗)93!=G6s{3\c]'ޖ6f^x8gX6J`?CQA-:^\^mr<g~qi{Nˤ.ߖs}dۿbӑw+4L㏜t8 {Fqߡ_pѼOT 㠷lH7q%|R]HMȷ:'2A K~ 1_ {J?<\ugeJrTE}.t:D dCނnjcl\AGZ3[)RHfwȝߟށqD;Hz[xޛ易wd|[mۋ} z?"Wg=|wJM͌ܯt/.8dhSc4} ^ Kd] ;Zo,c?ɲ\ o3X\~>r#)*ܖ?=P/[~-{ᐷv|f̺r\ps%G歵u[]8Wsz1,S^ߤBp/ӇS?x}%OUh %٭yqG>UHqS!O:oiu*Js ޟxe0ݡ_KgM{Fi:x,lKq$j~߿Hxn y|xQ㺯.AnXEeȫV`>ߝo iQ; >vAN^-HWQj6S%Qmٟ}>Q}HiA-Ͱ[<>kG|gY|nDžww[~cռW䯑<x3WYXXz=!Oz,v%FJ4ix s&-Wtwf[#S/J8ܑG} [l+:r'=m;yl7{knocOx.ż&Kn ]6׶`./ۙ-tBt6Bazˎ' WL*A%!d2y Nwv뒰2m=) nUuz?5VLIs~ [8f5ϽYs7rʬ$R$Swx\ O!~?Kw0MLەz xO4'NXkҏr$dH[ `Ϟ 6~m~ |Ҁyx=pN=GȻP^Ë լTpUm HS>E1߯V;ͿpN[#}Uc}I) N^{"xUR@&0%ZR| u浓=~Xo$䏒T`;fʻ_@>l9 kഖoǪC>t,< s~s^ޙk /[=7E,|O\wdQo6q2sdlw#p:I* hn $+1yR|ؗ+PT$ e'̘LAc:*SD̃"m$'dJ%s{׌?澿>{Ym tOhywfoҙ -1([!ѢI CD.y-kNJ _Oa-'k;G/ߢw~O_Z?Υo kC31/:ʘ%{KkQV]h< N~VQy_D×^9޺w<0~]ós@!}нRa3{u 9ިAoha{*6\  X+Ř:L)xG47rĄ=Fv>;^{`Jź{?Uu ڱc!Tnj>;I ?.ܣ~lw.’c|`Iq^$]/~sNĘI.>.dz^XP?JT<@i,^' )Fc(oeRg׹!%x6  v/,)V8٨qsJΉ᱕\ȟ!?ռs=Ea 6s'1Շߏ>NQI֧7p} fyc[ `ޥ90:l]J3l JkyuKlWhL}8@zRy7+pz%/"8xq6 Ֆ!샐OSrNO'yvWe͕l,׆!K܏Ya֣Nut~`>c$p_MQF4+ dna_O`IZQh4m]z.yIkBA}>zٝBZX ЏP(rUg C\zd^BQk'Ktp'g!˿OC?(1(},RdPeg&viCٯ!,٦7JG J'B}IAo\>8-wXgԯHH]ݠw'KJvsLC; ǃwKԡY'|~+Kzgh}<=wCɏ$>_W|PKFjϝyȼ?6{jNI"qsmi08| ^40#}">[NAUNmN!ץ?/:^o,}oW`n#9mĤX^1Hq8z>v<c/}֗=R#abo u}JW):GK9^321{RKsSBA[i<]^ze㛔ж]~9Mqm@^5.,ϴJx& Jt^|M{mc cEos^=E6 Rx"pF03 \Z{zA<%,8.;y·DSI`nu>Zagq55V 4Cq;yg/dD[mS闩M֤y\?^x^V$/nc)\[ wP )H̸xtͧ_Q$uuBۀ9NU"Mъ|s o6GeW򾪮pd (ܰܞSq\/gEa&CG33¾}> 8.Nၠ}|LVKzP~f&Co Maf)0 *8o7Hwq gG I ֎YT/M8?!^H<\d]UѪS-c/ł7xTL}n?s1_@]qvSST`~/Slm4m^GG㱞ps <^^\Op8=+LkOe&¬Ms ~w?#Y$^`n̗[Cl{)6;[grjYrg<Ҭm}2O9CͶy_f.scÀƾip?콫T[ts:d-|}'w 筳[7O=m>"~v>عƾ5R DÓt-oC>n {$?O}-ְǥ3]h ^e욓f's.?{e(͛Swt.j \eg)}|9ߍ C8k'\{Jl^zGWwtb{u:?mQ>;CIE>TwD9<՛$c^)l4z|ӌ#{ !?IqsY[)o\x/>F׹?I87_1o1 y?roܕLqS[.ݞd`^G13g [&8~uZ 7u8-+9tgC9H=W*E> Ra{#Yy1JKexG2r5%I0QemSllz=r{x?͗Ksߔ\ r_W$'n`y EWK3~GD烥x4w+9J?JI$y|TI./):dgw06JjsG˚MGv_2UQ9L]0oP_&5odu.:6X(.Oھm䭈<tcpMcm?秲סs/{ץ{`{'* N^{d;W^g\*[ ^W^ JG)}gO.e U~![ߛ=K며.{?3W6fIOSxS/wAEmǁ|ӀFsxիAƵr06#2\Y޵ۘ2ؘƶ 7~-pyH 24]o.jQ2rޓ ^:I㷺/R촼ƻMv1=c̣6{PaI'|/)\k[ΤxzHÀbEA}>l}[o>bv6[NdW kuL붮 J?uxMIļjyUVT<- Gī7-@);c}H;k)p(xTyDdor~}< K~ڟ)Yo$~6ǰ쵵>sCiy}X&>k mu#zAΛQp[xE}oF_i`Jٝ[C'm4Wdݡd#K[tFrQ/ v#r9 ^ T?} ^9@T\8b}ȾoowA~y]/t ͸6];<ցW|Od9a&]f>XKAS؎{;72E q_uA0۱ if;F:o* oRx¿+|NLw1%g֧p>%ޫwkw_~wuM—}B[*9}\[9Y]Rl mN~G~qϞg@^ˁyk r=G9qH>Mč2)wn8]=OP'̾~g%/WE}" )=[ U9z; Mk+M)ϱZ]BuWk+@a.eٗv<;"}dz#!Khۘn› ^C}~V RT;g/(Y ƪs(m؏kWo?x͈wY68 "2^$r"i_> CĻ/3.N}?+*ۺ#8~1I^:4~0Z<6ϲ~e<$ ۞⛓ V+RXbvQx*1zqOF)\+Fs][uV}%P@JAJ@t ,!tJ"JwDztRJw}w{L9v\SI Ӝ uִ@~Ԇxoc[nMr6'MD97>vME838/ _-7 8۠kkWc#{~v-z]6yKm[n‹6^{n) Ք`Ǿ5૩7Gמum}g3eo]rz>ӠJnlý(|8]o7~o‹k9*]C&j]/vhyjykv9;]Y*VsC׏t-&zPfl0W@غ4!L )X\a|-{Ss|r~p4I'A[3v5mqti؍mcIt?ch}vЍw֭}YWN킱u<nvC Ak즒7FzTVb,n7vmP#ot?g%u+I9[E2[Xu~s~?߸)Z<^)kmt1mTM{@}fQw{pJ&8&1c^:ubw$0`}w9u:^w-kvzw)c?pOb'_,˴;'=ƽ*%R̻Ǎ;BHROrz@w@Y}v7_/}vNra֌U5%s\G;*pDQwfγM_e;aTǖn6r4}=}H6i& O.MT/7ӵ>=XB'5 ]: u"$\y, ]:`֗jv]ѩ\^e>l}ا=6A797﫚>|8tH7yweʯ9ە϶k.>Y\O=<"vl s`ދ2ög/,j͸~ tyE }p_';>3zؽ*[Ȏ9xv\/,S=Y_#0Gk̹Lof.nߦ{)_Ǝ~ K&RLc>Еru4R9ˈ|ڍ,\?TnT&^^m &܁=%ؽ#-ȏz1u誈tF{fi"|u~Α=K}5=p/CJ_ǁh%"cc=/ڤk5կ^y-G\>9<-\B_ 75fC׀tyw8ܞ=-H l1?:^1`5;&RM\f1^70Ͷ}hq_s g5\Ex/Osp./SSE |Wxfs7 4;9WretH~sys}hWjڕYpBαyw[\l -zj~~ vAs Qֹ߼CkYonhj޵rS~7r:+xूw>)8˅Z+ |Gp_-\RpUQ[ ̿"x?2r~wşLmQ~N;h_Jaw_vm׾G K8ҾO>P5$ge^*y#CL[Z ^L|88x=8xi>"n^CA|8|ya9PyR'>ar)\HvErU L kgB&\c'.,pwgq/? t̉kq/H?պy\:pyZaKHԯ@tr5Y]])kpz9?_ş(K$WпN|Vp ,| x+oFly{6$$0NTaj>[N90LUp5Cu=Nw/SKqO\yAh~B `V $4aMfO<@\{-J/F> Cq uٸ_WU )SFî.=;19>W{$;su]~f0R`).5ʼnNQ];5MG:Usi;kJv)u3]m:hQO*>w6?>הyqt*a±*R[B1k&u`ɻ.&M4ml8@<};ҏg2JϞvO;OB߶AGa C~U%nO*0 cbt&uKI\\.,M^7Z %Dm13#;m`_,?~ q{~w͋wՈ M7pu?\x>C=u#Wj".O\ܐJַ+moͯ34|DWnۜ;Q~mCqp3 xr"VVm8_t*dpg..ı4?N[̫5G:'G^ĝ}O+?6@+ HhL<eeΝ^p3 ކ~ m]O`U^>شWm!k8s[}~t(`h_I6<8dY?#Φ&T>a}X56C ]KoN^4t[z nyl=ai]{%t Z>5tv>;KqtwvU)t{>:O'oM\8qvȏfPu8{yjNe'އxYxen֟"}>%^Lql^Jz^~/-̻c;Oa+MZox6h^K~Cpq27kK" .C< \xs+q<^>891{w/r'bA')oaǣ?}2a \8h/3:/I\m7LVFWuOaic3vn 66|ʴ<֬| cjN Q!$\ڑ}۴I<忍A]'/xy[{ByN6.ۏ91Wcw۫w[o9)y_.E7~TQzn`^O]^y_6'?C}Ι`>*p99!߄^(?s~CΥ9=mAX,`34uA@ &m~3kz >#~Ss'VJ$Pn![q[%Y"xWk~oqyb1kYE_Ci9gi\Vh n"G .x/gCuHIWהEiJ);8W\C+X^X<8p9ccYݧuHi-&\n@sjۀ] A'J5NDz~OZo)l<$?7N49ȓatO9c1z#0/\_g4*6ivig2+yϽ`}`Wn0ǃyS@\ៃyK06-s{sXAͼ7 {oyo.3=V3L~Ϯ}J3;US]`GuF0#|Qp!́E0/ų?'7K{vXs:, ą?ZcHk\7L\W6XJeOs3u`?:潛[`3IrD3kS6͞:a[τ(20Ww~;%tofvc6_>}f-g*}Vove(o3 =.J0Up3 9(,mE7Ip?B>5 {HGy{HL\%EE潦v`SgXg*S y$R|vOf5uy~g?X]=ҝv0k6{ϠoHiNh~yԋo@ߘlGPyKhkԣ4{Lt {HxS65'*ަftjm{"jftǠL MIg|rR뾬T4L}G&ܻ{`#se&au=ʵ3뛣VĉLýg_O.)_uOJmtI~rB6&נt5 kNپi QuF":_7@?wLim;^'ߪ> !d{gOf^,|ޯ"XﮂN6{avC.y/!RZyyi`w3{N(0?S?0?|M^up_HwO{LOT{?y)*qWuye&RVyd:Ny! 6oYNsY:Jc=߱}^??Blyg0), `;x4BsVS眭 S'b0/۶!Jy͹66[F:p#N]%}Xy[yoyO"{?rkyO3q0C >l0C>D! 5W@͚\qgJw10@Kqm(=Bskrj2"`^*4[f-6t) ^Ϲ;8/4?S.do_Lfp u֧Cc BHld [kI>ozNKR_vyy|p }{KFv!q0WYjhcTENn|F|KG"T=N4=X }RtՂEB^}:_ Jxܔ5RoAщS4 MrYNF9yw賒]w6|1gΎq?szH4s&6>唃Rpf.x҅b yf$SNޜ̺>(JkgpS.ؿJ'/{ۆDĤv8ImmUZmIɯh}ag"wkpd&/lKQ2)q S<3܎}Dul})Ֆ;B_ƩF{Е.Ǖ1Pg'_~/74t+:u䇱[]o=8Z@AW%evUIEpBa4DjN}55 ^:MU4ٲo٠M^5ЩF`;M&*`k{ ÌY?C%ʢoYg,Nc`? }C bXCa׈f:~ᇱe.}J,P]?'}k/~ğ6} /,ac«]L𷂇!vG ]A׉l]eI6+h$Y~owq3]1={8}ǘIk}t"5H֝i! }_Fs;SvÎ˷ duۂ8+>׽͔Q~9 n5e4<6mbr [oӘ1VnҜ6 aQ;g*u2>t)_j#i _foztSɯaη؍y~̘pJrchJU!}6NMcԄ7g| J#ouw&/fϷtIm[߶ug8"r)9I+.[Y],ߨ[ڧw+ìp38P4I`ϔ_?ǯ WcxCn;6}sAM|P>|{t۵$/p?<ŒR[;Iwl9{p>Tw5{2'7&?JߵuCJu~ՔpH~Lk@IGctW9a-C2%2xy϶n:;Μtgkˁx?$l^q q`S&yz2ܷ[tcui}mov};?&¿{+aסd.AmFdAyKEr`>׷Eo7;j@\<>{`"NQ ø|ݳkm0j/9Ù:m"R}bM~e'NC(Y? }1v8׆4zH rPVǏ^J +J|$a#ozoc 5cJ L}R {iJeG||~/i3\%CW){u cY+12G~'.W{-Ol#MS('|b|}΋SG&MDzs<ۯƶvwk;1t }u4[* )L9~&}*>Sng*ϗ9poJjOu<lScEX3<^6?"l3k o f}pZL> NB}_<2\'pVFTF/SpWma0ǡx?gmP) } wj4w>3nt78'5gL՜ٳvO@r~kb_g r**coRnrEci{ݬ/VpW'}iNYCcЍue=N^]&(Y}j%$q+tSٝcp&w,3-r*?.33GOfʩUf6- , ̧4|e(p_Bٗ(˷ &_jޏosc~'Xv˝|i֛ﱿJ_{ Zp_㤭}&F3#/cQd2㶭AϮ^ fk;1=>xRN_v' SBw{zDjECst}8#p  P}m[J~? ߤ_HQp?D%_r{S}.>&R\8o,GxRDdx!Zi}5y;Q9Y@oξy58pZ"Tg|H>oDž|$-=_ ĵ9K y0w|٥䰀:ÆITg" I]Tq`>Â/yDOjPhq5oû2ؘ6u|2͵6E(j>.\@l;ڶgm׆?{`t>8Yc2A\ jHOT(?s"G0[67ɴMNoAN~fR;l?{HU bwr+@,:~G8Ŏ~m{j;+l9Θ/$ڂ Jp?#OPj;|^$k]ژdn#f5(GyQFTO%+8pwW6xS_{~Ĕn 03YpB^E#k{7zgؕet%Iw+ܖg""#n#F?T^8yRTMח/yo ˍ{_Vo^@MsԸzN]ņ>puh _ѵCEݮ7;G~;#_"l:<ڪen6ՍxNޣ0 "XڛثZWtgOIm:*$ Zڧ~HMSmڣ S^Y E!G6 Ɔ6k;X4 H ﹸSk;/r M2e3T*nLJ&ӺK 5Z|ɼiu+2oµ475|Iyԝ"Qy[^wk¾oS&4RNnGwRXзJC_=%Ղd^5q~v4xaj+yQgTrI\_k;M]w\*}~}7ff\SoA3z8m$X/;M裝z{8woG@Q#D9.c"_(# hqOrnagAZLnΫǖ`7nͺy&_\OкA33mz<!N~n6'tHW`DqLyzЍp ^ D_ϡE`F/Mb3|E+:N6.l ?ͳ૙ w`sCזw컳."#iQT`^#v'G&P&ѿg,/~.cu\Y&r:}?I`~ N=rgp&Rd@Pf2v_DR8N{yȠe쫦>cS>]i256d5W8p_>:Hʩ.\VrY2RJQ2-b_GNښ~،y=tOHISi޹5c۞-<?S5xӚ-2\}ۘ ޏm ]}Z_ \\=v]T_"xl>/><7M<ڍyt(k^>1RkN^BㅱW!AF^~2rNmS2uu^ZʮRLWO6}x~W /nxu7\ڦ_ǶiilXo^y`ޓ*xyƬ9ʭJܱA[U~!ua*AXs+q;߅4zwcSяzV>gZ*0~g/߽})XS< o_3?4荁.t oNkt̿>KD: ΓݛQ[t 7[d_g~@m$@<7: i8m~=|%r6eҿTNy H} [Akn/O9%jfp%?.E ʮ3" KH;$`!B( "RS(& % *"" DT.E@#"D -,Epy;̹}|̙sݹ3e,WA[u,skKvSsFn:]ѵ{'ݣoU<3:#:ğ4KEe5{CmfAjjr/oM:+sOJ}8?DNoY۶*sqsoH#ܘu Fe5or@{ C#S֖Ӱ{y wNǨLOu» \}6M:e'di!2nCix&4R,k)~6"Ð99iAܝ_q zعZGW.gY^I_r|J[s@\_m함Ӥ1]:jAOG^z*= tIGr)/5f0pxF0+ gŸ. Oڏq֣fg3|뛏66与?̓& s{rif>3 8ez :.)g;gT: ϛޒSeJ!Rx¢Ss9byoFioD_\_ %T\GyIUsJ+׷C}7'AU{X RِM/O q>٢| 'p>\.-ppfynRXT|y6>܆ rs){-ﶵmܽuu{mv/"9]+=7cOP2)rn7y <rKG-򞂿4f\3WJ \ًe|s -dVzJ8('NS|\&dl󺈷_xB|?b/3طsQ ox_J8xcT-ݘ/#' *X\Rj ?~j-{u#mޣ`78:8?9'tR)E:%b#fqH n)14 {. ?@yevHhE*piҜ Ϫ(㔎HU.p}zNH|;`O W廳 V n[)r[ٰ#xH'z!?>@Ϭd9#N+ _)~ J Q؛*7 N5+X ҕm>kVl'+W1U`^>_U1*-#l˘t *l88'%\_o <'Lœ2/St4f*Z0d'Y'+;H.\)iS'L.QZ|ۿS;2˘7N^tݘUmYS:_%dG}V`ojVUZ \/%So5)h#x ){M}rIu5ɶA.6՝6uG^TR"N>6)t$IW;ܑMIK%s)x3D23%dm2﹢{ȕD:r"ہ?P֑'UP9!WͲ6 ,N>oR<h״~ pKgzoaâN]Rheϩ~Dky{){{,o"$\pjvpɆEϾ-KAiOg.T@ؼ:ط~[u )SkY D6_{>EզZ5'#&>BuTXm۾$Ձ}_H]m ͓Cp\S9p^An.=^ۦ}19m?,>$"sdπnYv>?l  SEExKZfg? =S,DK/ oc)>8M2.^ݞ< Oڞ#* _on߸zm^iۨ-V N~?Tቊ3S_a%p!oj`v/pEE P2kL,.pC|mHcw"upu( =l^Q5\6שUzg_־:<Ĕ}W(CzFYDU( Gٲ8MZcu-\s#3eswU -. guKDZqce]:S(93$k{^{Qy.zh ni6}FMuǂ߃S?uhk˯#wPW o_N~LpVEX\GV Vx¯ƸLFcCqgC F6\cctY4n>.V-m$=U,.p][7r_c8},qSr#|(Ks pXOk떈X[|(E)1 >3ފy]nSre'uWpC~LM*󶷲|x3IJo k@-f|[ƻ7~;9sXn[ޱ ׆wI_KBqgV Pxl{1G-19ݝ4l~gjSɈ3f;5&k< 옓Q7‹6v0qȘ~'nAy_y@c9:dгS|< JO>%޴5֟Y*}U$]'[8B3p~Ϭsn asm np}'멼ߪlڤSOEW0 ib3ݸtL+Sx'(%^GFtrFoGoU,¹ܣovyRV+Szyl~*uB|~=Sg7yדVI{\`eWyM#W9ױחKss،#(-,=U>Owl9tI|3>Gnvan4uxUi{WBaGUS;\d{s.1(o ȝTr&Ps6xȝ";;B=o.W͆d,sti6 \eIܒӝ?;9~ъ|C\ʕEA9#;N37uUryQN쬷K"ۖEξM*wsoG2-)ɧO~ 7ZǺ?6;hfzܛBi< 75}'smf |:* {D]@^Xs' )gC-~Oxp꧔uVֶ)!_$d6C柬o}s2*;V2{߁\)gm6l^jPY95«Qwp0{ fݨ7*3x SXWHjSC]^9&y}h-+mCY;+:~nJUU|&3uū+(ܚ{M `~5 #'93߿xWKޯ%rkl͐konA\t[ֹM|gW2 kmo\ou` ;(;y&:x^'L#"I?Wʜ>\/Z ke}-<4Nfux(w< ^wOq~R_ET+hX#cwE޻4RU;ƗڐOWp7ovT#Lc#1 &c/?P^?ё\ mt xOYb=E:P:OSK7OPk'(\z K\&g_[7s='Rzof!d$#Kr柌X(w2E>8k(<2LNqo|,o*xTkގ 6/([ש\YIt<,[ w;{vI~0|*ϛ|+gto_N}" nvØ ֿjw;|Kczy&Q~;bF[|3>>~^Lj[81M~/9_7|J1g'{=&<km:wPR']7Ը #[@2̰8s?e1 ,c(7]-AfU 2K(S\x#տK 9mo&y겑[i%ފ6n{`_~9k6y`3xs_L3':8z1'Z= lZ}^h:ӦAng<'rsW[k@v~Tܞ<첕췵=U*Ɛ{b~{·8#UܶCTq'd/gZfcփwDå~R?e?^RXy{kNt}OINyYQ…lx yvˤ/{H}~D[>;{P+c c{-&x:,~\q/)\mmCFMCu{O// \[> .G/o7ű ϔ ͌G] ګ~@ևwuhN]IǟՌN:E/d$iӒr_\'| kok <'9x ŀ{+6wOdB; pmT ~>;E/*}Qd y}f~Q)qk=TGG ^+]/cWclbƓd)RR Q+}Io3cfa:lYɫI$ mS!-"$)[ˋﺞ><ߏl׹r'֊jϣn3~1f"LnPi#F$Z<(ΩAϝZvqz,w)+7P|̿kvR$~̠[ΓS"}:Cuqu&:d.L3rMIGTM%??dZ^ GﱹvPJ'ݤy(lHw/m]kܗ ^[w/.soG O"WcdC}{}cYwOR6n')nD ݞrG"`[n˿,zxAQd =+tORg XSr}q~DN"rHnֱra%۪؎׃J۽l{P|Ur\UܒhV+˒?1ZVoUd4!jD;wzAG+7*w_F9ԯWH2me'u/3#/-|!Qe/Q7x+=ơm 'w\{nd^/4vMi+ZOdy+^һڻ Ł7Mwi~Va -ꐛF;˘ZōK{=td,fmgOyfyci,}M QXy'NM7ζL)ӽx;Gp}g ?_7R¿8.~]#A~!ɿ\eNqۯؔ=F44qK5?xO_u-'^}&Ўؿ^qftޘ{[x+$U1rۚ9OS[xw+:x^#^%C溮ލ+ v9An+9>~ۙÇ/=K'F|?(mgoExwx̷ Cdqbd]v.A8nU;k#cKpqO-Yזl7iq 1}_<ʔ.%}nU'$0˿坛?'}?ύC}0=X:d}~Vy%&EJAg]-[_\\>$)`x#߫t1Cg+f+QSJ z<߯w+_* gsF0p }:+ycȼ5@Uc k 9yOf w(v?JFk=(>~W=mi|~27ϓ6|nK|~| w*3%F1j鑲2𓐋 Ǚn%ǒhe#W^Wxww|{t4M6!ٮ>COϐWχO؇ܥ`uXp7񗇃1}vK%|ҽ|#TPں;[<μI^*9@u8p*p"C w~\( %Li"9OWxO:lۯ%v}_u)R`mU#>Ns?gzX^0o ~=s|q9ݦg8NӀgm0fq7^ l^3f> 2{ZG{Sʷ7% 9L>p#nI׷*YI5ZLyi1նu׹C5EG2VodZG?Y ]}ޗOtWޙ鮼]y7( _2 |͡{Yyw@}2Ϛerep7'53W*%˱ rYVU]|VBpo煣$ϰAĿ2Õ&m43\4-,tVaL W*=.V"m4c)q\>vO0}R4"pkWQgl+ 'D(S9-G%ej8S{ZڷT$|{}~F6 Pv} ט]OTx~oןWvu8!>6*{C)dEi~ dp6q:>j -rw&wEB~b [?_YKB[HL]I>9cLe(R[MBQMɲΥ_7*eS̡i2(M)\/4?"⟦72Uܐ@:E'et bo͘l:JnQ g.٤_ڏ\_|8S}% FȽ }Bt'u}k$s '?<|khn)~SG[ɖx3tB:gGrzɉg}[ʰ=|G 爫~7z;>7TarI]͞c=OIýȟ'ޯ =. [t"JN𷄣{?x[cDzO(~&ۉP`_~dxκ;K^w8FbZ~vwu?İK#YLW//g:v]h]tx ge.g/3A?Cr4u=a PL ~pc)=<2Ș9dMҹݭ[$")j g[tO`>c&۶ c` ;?j)U| ~`cπ_:+8/~JO n+ć(~MF׳k-'GǨ.C4L˲~IlgIXwl?p/ u#<@s/(K|= Y~l4̘ĿⵌRD_z|ӛnp`K^R#-3cůq ޜlׯu7dPV"RY ^-S>;\Rɷlx\Dc_Դg#ϱK$]1xXU1lb7辜ȢmMU6/s횭jMs!ArW^A ݤRmqC\1np/Rq߿_69W֟ p16S6(,x1T^֣{|=7ڝb嶐C\9oU:_2!G8KCyw}mz:ݏ;w^}HC-{omoEOjUn8_4'yK3{ݲBfz1)5(|)Ge =g8m?1w0y>w*gN䬘wmwBƀ_KOl+}7TڻT{*.ɛ` ٳ|f=e#\6r4\#'DQc /t}^a=-wP>S^i?0?Ҟ]’C.!{G߱[I$_twoG=߃(xw9#1Ζk~:#0ﷅ(`ރ·7e)Qzk{*ݯlʒ]yxʣ'CmGs󈿗UFXz#o=d ;R {>1|%rx^^<ey6ٶw'y]܎zS"oLς׋/i1+vKGrd0b4O17{u)^ j^(~<{>0njtY /sC|yWuϥ4RG>_c `ҙ8;8_ƌ/J/-E(ٮ_7"ym*xc1[XOԅ죬o<6r~)>ﯓ2JHiZ=L2 F;_ u9{1I*<x1O+? ?Px 젟?O'C~8;m/&wOc1XC@Srq;dNv $}MJ_ R"ř8FK8nW_R Ət5+Vr/ކJ5]LNj^^֡_WG+' <ɕ-?M #U4Y7kCn-hTc^K ̦"N8>GaW_$pP8ZsOU'u%-..>ޫ yS:[ZX8|.SqZS@;(\."g-9C4 w;H8ǭp3Yz(!G]A-<{1k,i&t|MZ}jDww$]j>Uv~DE'?|Bď$r]jM_b}wAhUX}!⿦i㝽,s,u'un0z`~& -]6VUu}'&d\B5`~.DNS]3p!X T7'ޯ8?Ot4ɕI\]S*|LvTc4!p?w>S;d*3^h@pޒQ4X H~J_dؕX33 ٷXCeTBɇLRf %f`c֐}  񪱽DReI$KY9\ϙys~}~Ysu}scD^""? dq$hGjIs$[Vl}C[IbKe_s:S"[?>J#++xm:7v Kfʬn~C6wz~5v+豳I޳|cNc~~ dkQZfk}q7,ǷXP]ZoIUʘ:iWM}{h뛲w6 V3Hw@ Co@8_9Ѽ2k)U|k'ڲ|_w!ߞaEzSw!pBwS\C]7(2„y7R&j^/ 6p|'3'6%t|.-Ne0-3NP-mf8oMk_r =N8&3#kN\&u>>U߸0$-HrQl \XY A$*&m$ۿv&M o$_448o{97y&<~˲4cת"~=sȦeIwix?U| 4w+¿,kW|'¾2!]!yw:v9^53@ՎA~Ô&xİiNNc77O{5^xۗ/ K:\p+HӸ[RkIA gzN)A>'t}+ׄ3YD4lC4WNsq[W%~,?\Okuis{] UX_xa]_aHM}a_;Mcw@?;H%]~M?bJ$>bq#7 c(:[p-./xyVw:Gģv'ܝdjN7oq($XRj;ռa+.CՂH⶜gàG 7ZR" $m^b*].-c@or`\ېLr24 LI.66?dYa,۶y:. oլl\y8ƚIV3H1>3 ޿qy|Vk{nw֣F3mG3m]ȴzz3Ӷb5KVmMcC !x"mi}Ƶ([cU%}9VJ3҃%ɢ;%C$5t^s6kn<2?ͦ>R ໎d(m&,ϿC.䖋4oxu~~8yWʧ6ew P[E^y|@ߩ5[wM}pÀoR0x 0xk㺎f_ZȚ\oИf]HcrIlAԑ7q2PpWi/L&Oq^A o.^!^{W-d{2U_Oދ~fFnsy9UHiHamk ᄎR?}!ۅYUpd 5*zwO?YF{NCZ~z0dE:=!0eYMC?EW|[>W+Zۮw-שFJ]ϏHVKm~jw@|{`3 B iTQpҖ#f-GM7ƹ OԻ}0枇](߷-pL?{\Fyߋ;OV9 e9 z )]cx1F.dIffYC:)D5@ϐfW>#R(/?bƼǢ 0h ܋pGo ,Q>\a0M\ޓa2\x.PMҥG}d ng5Rۀ'|bˊ rq9{d~7RQ˭>7ύ>uH藩/wyD ~(?~>lR;!7rK9~On<9?locø_C~ ɟ;z9H~ǵBQ~w.S ߷ɖoz**%|QF&Tpfti9 <'OmƇglVwuƩA:ˎmk; ?܁!x YYO&l7o/-Vkb)m1پs;\~.v;Ew%m K]`;^ - ~psLk?Cx$ޔmMQBe{u)u_^kqMʰ~.ףnHRvT5J\' MSzE}OaAvve=*墈T\ȿ4E~Y~oTɐr!׊W1` 1ڸqmm̋@1q%-NKHo3onwo1%HN8kJShjY]_bݽdBˤ2bH:[&֍)-N,= +pPn(U5>Y&Ox>[ΦxmgQUj/Fw_+w!Y<yhc&VoF қ+6 rI&>yUzT  ?1n{?xf@u3B'BnF̝I]ufuڧ;w>x I7n-/sM eB,$"赑=M%!`<;g;BWJn8*[`yVM&꫰}A&}oQw^zDkRW=o>1ͼ?ñ=}ٞx쎞u:-AWȿ+AItg{?3c/*Z5mCwHBQD|&-vc(S.r5et*D_M{: eNro^_!^W9|s5?)L~L!/[G\})×7j~6ʺ}oa6}m|t#cyי4osO:W"e;\c o"sy~xS9Ԋ14^w8GSDWǛ,ͮ4\MsEs rx^gY+w;c mzG*-%$JJ"a(QMKd~e00#ƒe=/ci~|V~ι{TTg&>~c6̣Գ7k5z9l BbIx[7?0?g ?3<}~;Ljvy> mƷ_fSsi|iy6cEx.rxH0yX7xg-qGڻ {_߇VxC;Ũ7;F|?Fǣ"=7ݮ3l ǁ_2{x1hT+v] SQUV,/wE֔01wn Ć};e* *8gAȔ%#G.b>xE\Po/}>&֬1䊓\-'%4~gӝԛꏔ`E$&% SS68+孋4D|`-G_&)8 Q*|˻x(|\gKKԵ4?Hx\7wH~E}e,g)ҦuK^_"ِQ}r7Qj. {k]AM5͐@:ӂuw'sU"y'B&dy9_l%[ Vcsvfٛw?z8m1 p+JH4I:W##"kểkB2onU&7ets*s-T#qJL7ǫL4`r`f7iUgv@mRp|kGˁ辰FSϯt e)?Gr i .ۧ#x2fdy*ڤ\cҁ%^pu;:^>| Ǧ'1,;Og-[t۫FqyyO1g^^Y-B~|~$5 <_^vp(wB<_t8x1 k3͔H ͯ+ǃCzfߚeJ#FJ.}ʐ3O q*u#Dj+g={+[eĿXg !m)?^k%6\Qgs.NECCZB4܍˽9v0Mp`46q}<1++.O%69 y/11 @;N} w1.4ٔpeS]C|E].шDRޖ–O8Idve;٦ ٮe=4Uٮggr\,a-|-r"G4K4+4^s=3P–cJzq'r17;a { v圾Hz>!jf=O EO֯fNWwu.楗/Ĺȥ>^y0Ǎ!׭Gr^|[6I 9-O(e ymD^oD}' éoL^sEvol/=R2; 8G谖=/{=< : nokM!c)J(9vM[}Er={e>ɞUϤBmC}MSyl53~) O'LiNCp/}VgFWǷfGZ9 kpKyy1pc?Zc-$^G,uvS%I9W!tq}|?+'w 6xu&fNuira)ר2|no-wƎ௏`FeSBSۥ-x'=xR!2fX]^ooO*uuk#yb9^!c|qSoM!F2HfNw$\Wr][ڛeNwsw+#q{S) 0Cn@f́76Y;b; ^w%rz W@^ t(E_.P*Hg~E4)H\*|Rb~k*(\qO1޵4__FF 8'봙qv1 o0lGu?%6Y<|Q.0yu;?Ux9 Q8_?9\Q~X$(.9}}&dצ^,'63 xy Q:7[ܰ/޽$sf'__ǁ;orƼTŘ5L u鞏$%Ͼag&'1wׄx_}p^ N Qѳ00?m<ۮbgBȽǓʜ<܏ͬmL][y|yߘWޝ{o |*WUCnC4נ.%Ti7.L1gHfp|cVgN/ oj1)qk3w4u9V;2Gz41:&Doӥ#e:eSɘ[e㕍W)}}OZͿ E[gĿZa)d+7x]z |-v]EORmZ_B#;ǜ?;|Ο;;r~|[WN^wlWouR<Z8,UO7mjz33/eގ`KU|;dRV۞? ^&֓ỌǗvƹ~t1HGulf"y-~SɜQʝWyM\3='ac+Icem\v&;]NMGljj̘k%YdK7w|3OZ=! =wU䊑fMFLr{&>O:T|lj?HJo~-Uڞ.|C׭=7x ɡpxո+ur*}(KT7FKy\rt/!W_aOv~5^!}~6WO ǛQToo—1̯#|{ӘTl|_0b$i8f^?(=dC/QJϕS3tf`jXkR(4nȥy8G{߫+'.IGNuύ̏󩿫W]}C ڞ{7ď_|tggkF {7x~gt~gwf̌qm"TBQqNqFbfTT:bT"2 ;IIR%a]zy_yw{g?guy1Lr/#~'o6x*ULh{8yxM~N~>+ՄZjwP9#nmal\QzE 2qv[ȿ7=5A& yPѦ^]?LQcDX7IoVf߶ax1y&|_OQz=_c&+o:`ߥ~ ]$8-k~?|Sjw.?gߚݤk>z>Xpzsܻ1yK~xNyHΨ'qvHYG%(۟O=$xHv6C!QXsTwU}_]I[߮1qv}$x'<_+$^.pR;m{;P{ۗo6]ރï652Yl;?KBme6()4/чPg(>*g2|ºNv'Wc׀dZ=mp]';~c3+T>&+}̥ͷ㚴MտNKy;Bis # oˤ~Ȳ>U6m(xrf˝rU:2ֲan"sgr.@KeKT=iy$_iv2T[vclRGzҕ Ǟt~No6t2=&R_zMivNͱ/xo2N \?x^#)rBWWM ١0⼺S]f;{XrS xkbOs  7ofBLaLPyS;zU?xof=3,Qe:0+0:u}bF79M>?Sڞ'~:&1 v>_ւ"B,|b[N7Iqh!|g!g>myy\JX.c6!>}bU %bw,;lixI~Oq\8 Q6xI.ոUBDlάͫѧ& qjkJauދ'QZMf)P0*g̪w̻鲨|Uq/_aݶzt ߇ f[d\z5^^eul/sK~g3~ > KM1;l?Pғ5%^gO2_L;Fݏ] #Q?slYF^1|dTw.=>iQt,+)鼯oiJE(WNLsLf1ע_";o^Tfx]wOEsJJۇ^pv{,qu%kAzC?se}߻^/#tރ>fO/e)m]!#VBOL7a^:VW[cr(8ʯ0뢡8\\LɴkW2UC\0q5H]g@<&|;w xi$ |_vKu%<2|y & ]a\qǷ!}9诏z^G x?.ۥMgZ}Wg i&)xdyngq'Mt+;1 7SuukIoKwx; ~=fe,S!ۿ `U߉({uct{uYj|䔳$=>fygw>p*| -Ue|.Uȍ/N^!W?έjNyE_r^yŘjA8U;ˍר6eL8  K?}l?&t 2 e#' OoxI`uv^9gfLycm`^,j8݀e={O9\i^EP{U_F۞xG|4rwк2÷d}4Es{̦ys.-OKbwGP5,ۋ3¹/CӕnWl}Rd~n_tmAnr }|h7α">?G 3ﹴ4vCe_ڳ#k˛"Wl=[OAѮF7Nc~Ѯ˷kh޾*-`cbY :Zq&;w0v2 ̵>9,#G)>?y9F2;I.0F`^fg>~WnC9 "I->M ^{w Vo;[l'.lZHwg#}F\\zXpe̿]|S7޹.6xC'(~"pյ/ xo_i4ȕ\g eҊOs$5@`t5 <_V螌w59ϋ7YT~!CY#kV¼k7xews4v/1xoĵA~b[`&c!)̥>_q(z|$e_(}wYLGG6?'æHcw Gnܛ{Ar7)~`/8)PmGKŐLKM\+EKoYcL/џg 2N"pY8|tnPGWq'ߊρ紏w01_LS1(3)+|n ̦F| Ֆ8oПȹP9+thS:! g+DUuM5oO͞|Huo%}N]nC˃m9<^l֟KDz*Cvwr}$l۶]I]ϓr ~7?puJJu{>"'zgLp ~V_p{*(.h6oLJޙǙx7ΌlM\vR[`{,p}]3\]-s#}3}\Vz]|mgw U/[am*o*›"^!zlyWO6 \Q $_r:}BiDұDtP?8u@鼱1>(]ay8Op*.V M۫;X;؝{_I-& 9Ux<`>ooveڡ W[%'+cL|U?Ex#CYO1h{o&R rدZYX5NX'y~i\0?NXɕIq]S\:Y k{t#ſR:O3G qz]^mjH׈%-'gSܽNl~e;$|{{M+3$䞺 >]r 0Ts`o57~~gKv?~]1(|-m.83z ̽tw%xli|Y&=^]=?s{tL݈oL: raqF~ bƑ8Xў'|o k:G6\Cv~sͬ#:? Mp#E|K[v]dOi13pU__7y5cu[};,fvߙ4N]#9,TA_-Fx'Kx`llw!or W }Q-//,uweb/q?'пus=Kmr}3u T=)@/xODvqtNwK2ch]h&|Fe3y aV퐸}1e"0~L!㌖&٪|/A/?0 ~ro2K*o *oکfy3vb)`-}(oUw,Y>ϥwr:N: tǹo'HrZ~_,΂s__?^fKz X*dΥRܱd|)UqBL?)[-2s2`rK,|_W&{,z.*Up?r-g\o1'YVPN Mѷ1O{vZQ^SlNZ~.\cq`7ظhs`Sfdq l%<\؊m~wMr:rK9gzwq86xθ,=˔'H/Gg/:6^ok?C̾z>nYx6sk#M$ոۘ+˭y/&K=f?ҧxIӴRCE>Zhz -sn,.}W'41 @\ I:64 \Q~v:c,laq%=t:wƕ~F|+gG~w^{q"-KiDo+Z2}-ދc)K}+9Wurm񕰮V=x1Z#2= ,_d@ܡ%BMfQ~zݮ˖ c^BG>}ϟ,c[+i3AojfzC߰] [ߓ~}Րߩηsݫs[ݯ#ױ^33Ynx>/Dms`Gśy]EuR3Rx-ߒ״>SYe0˸jYӵz>@?.G1>vO-qɌ{N;ny(c5;<-suGϝ\2}Gݪ9';{H`'`xngfyODze]؂潪0n~LgaB|l{YC\PJݏS)zoO_nJnSތ%9^$Wq?vy!"^+gMaƥZv`9vsni} M]{[>xG}/wVZ6Yֽ4.,Md^|n[|g1?]ٮ}o2]3q_CgE{qy1Vdّ=o:οc,LdSlʑcAMԸ|x'|c|Bf_$ʿO#^~,u9Ot[{w]g/}^9~կ!/ ]Zte#i=⮴0Z~Cpz h}o|OY9{u C/ڍtsfO{\' ^V>yvOG߻׵lUNǤMou[z,Az$|ϨO 2m`_2vK8Ny1q;OX{Oge /u/Xrn^M;m7HU~,| .>ih>u}3mB?aC/Wgxۤ˒u68)CYiXɰYי/߿/>"Z>,gesc4ӯVHɖs[Wr3.?W7~]^5p0eWdo3q'qn }\K+~r&}"Եw#}ۺ鹐ircvsLϓ`#Hσߵ6ٿ̟7Z:_*>#i,/^w.K0Ye>kg1}\آ렷p-^B^}cg)K#oGk>j: LyjspG:<H "p:l8~@SgKi.\nJ뚘yt(p&\ kZtkY_L7ёRO9=a$,NK5Qסg=x;|N zM/[^L/w+s*z \"R]1xH)XlnAoOo22)yҩ{W$z>}`Q#p=>@_e~k|\}B/}J >+}>Tܔ^O/KUٿRh { i#Kʡ>Cx<^M;- N/(~p/:7<~@?/p+p,]N«R7] .Z ܐ. t3ZU7+C*B*~NW Q)е,>)>F7A*7x%n_/ѱpʯt;Lw?~=rpH# iCz0&=AóTΓ7hvu=.Ay܈>t |!L.=[=^A@pNʋ %p* >H ?WJ?鬼@#meIzMCCpKz<$DB*FWp<^C@)#6@ءU+KGړ> MPN ѧ'9XO_LB.eW4&} .|A߄з]^o#t&r\L?'Od^џi8[9CÉtx)Ïp>ʅt] EoK鴜gezet <RO"|SR/{p.߸1 o-t4|ӭe_nFNpMw Pץtox&FOAp@!p[z<ot"?Hy?=ES&z=<Iã7e,٧yz.=DyHϗ>L/ӋR)^F6Ty\})P3ߤ 2NMpaxz; >JߣwsJ[7wA=\r)%O}g }\}.?RMv}ȟ'^"\ nC_O_o ʷw_S t&^?Mg~ȹHǙAOt.&H\ ץ=i9$::>DE£MJwet =3LtE8a:LGסl:t= }]܂n#tsx7@G~c9!tk5K}/];uq@ܖ@u7o{'>Rx~pE58E{=~L01x} }Nңpلx0= ^HOst ?IY>-Eg,Уτ3mV>߽T}ޝq}|.2ݰⲻqv_ga> 0.^Zg^\пqzP4OVq5O?H4O:ۮ_WZeDk rt'K5Z~fZCG>N7&*>Ysxgv0 wGp>1G'\vIp =M'N1nSϏ-˟5C}Lu8o`'+̞_1^_׋$_9>r'V忿O~u+|O\)cYٮW=21Y}E]F9s;X|s6]3ս}}<.(T~ji<xyΊyƅ=#Nϸ$^>)g#tx\jV_z~sԀOO6crc)>s{q/:-ic fxZ#̊6͌hk43}s%e|$'EY'94\*XF1ǾU֫0Fɜ_'_2|57]dr~5BsFy*/1g9G_W͸VuXw4ӽzOUuz8yfl񨇎[~V9MY6ƽ1<~V=?QW}}] {o1~=]?; 7Xk?1Izhtccו/m9{zR%INGky+!Ƚ L"Pv˒k7c!"BķMTBJ(9䰑Ha$ӖX%Fffbs19~z}n?s뺯7Խ+uݛ0`C\3=6͟a_Gqo>|׆<x6p 1{~ Ӂo? 88x p&ppo x"-iׁO7<x6Vj:'/ | ppw  8 5Fko> x8<YŽw ?Z̑4 p`91i׀mp HWKmw 8.ц< x=p m1p 88:p-<x>.S[  |'oMG/Nmp>88t/ |v#. (-J; ?&0Q y%vxC/1'MGk~W'_.pCÀ_F]oN \j7 8`=;^  pmÀ7 \i/1ncT0Y%<|ry3n}3| Z0֩Sb5ɢ~; &(-!}>;ag;Eݭ}Y//{Cf>ts2}mfZLrr4^0Xt! ?6P$̾}?U90C3muKE?VGzBd+?1C}/|)Wy{QOB0\RtI]p|"k2ByM,īyNa^{䍏!NsJ](2ˡ}[El txOt O:\a?s${-,hr[Kk.O;;SoM|weEw{],wOx,*%Z+w»݄;qT*83-єp/jc녷_%? 9-{iQX<FI۔,I:@oyi?H%/~RHJ#~Nv \6h>#][r"N~y'eR/70﯊m^ǷeCXYӷu>& [d3[Nu ?Ew1wk)yZ堥?k۟ꨉϋBq]tZ[vwܴINēg+,p ?o,)|0?CF@ir^6QE/vW<WtގQzt,unKC|R=l :].k} /{:?=F-uJ6B?awwbo}dybQI^E%#1X"vFk߿#(|Xd ymߏ:O YׅR;,j]G?J8K^wxޛ:Dt)+RMtyKq:djBAk2\Sxv9=i"(/O>,k^sh 2q7n|~;;_»@~߀O9H֡:-ۤ"WM&ڇxM$mo |0tYѽ_Ex*>9~nGK uĔg1G}e;rdp xUCE6ݯQC eǤ{9;ܿ ɺo]\kmJ3L3<[5'wA;"Ì}_exnTs/"ۚnjmc<]OIcyOr= tw">FamY~ѭkgI &Ccpg8nq&-޻{*n#UWM[у~3.to /r/ދ/ >$5ݬ,bBfFz~mkKDZ 'vߎ~#K^inYܐ^2bN-Mc+p|ez.Ss9UJҼoz~.bB,\qؚ(c~]rw(d{A8ⶻN;B6Om= E7ԣzm}uMAm}:ۥ(I7V#ͅwm4uIk>]-_/5YWu NI(u5N5&#{٢;>I]>>@542+d{0ׯDI!P4/Yhf*[?~hH_ů#Ҳ_u[2 "AYtU8Ǫ×)t+׊ɧGϙ>O N>T8.] 2<먾Yc}#B?-[=jDBյn_^SgoT'xFt^ex[n|ǕoV1r<IM?c ~Su?p jQA٪o{;ss#]Kҏgzu̺VyِC<]W/l,&=S]vmo:Tʀ>[9`{`Zu;SSxIƹ^>z*A%m`}^4S6~[ijE_l^[ݽ=㫖8klKT6i?mlyw8/w7/~ سSyYajCDO7XکvFq4oP)6OY&vOQۄk͢sV\nF7{3~s 1m,ܫ@{볶%qCPF =piosLN .{~kYSJ NSLڎk"uwH~/$%m\.^n'~ѷtBٺ[p<ƕVo1}`3N k~jK`tc@ڧM58m#x8X0wdWc; F9&n|{ IqFUEn"}q]ϥL}/ 8K DsFms"էEǦGc<-S|=\w~֖ 1Ht+/ Q o>oݶtuC+Xz" uaT2O yƶNw7u kOˡ>3}(a| .]K*an?%<1 ~}s#g{Slc]oHW-ߤI[['.|7O^ ؓ/8˙(bz=.|_JU*0}틋)7+pbW'5]$?Xߊyowxi#]OuQ~cA|{?k1HwH_ŻhSC5'ѫo6?4{紶*N[x02+P})|ӿLݑ _[qk1P=L|4g[v+G{߻S}(7DWt%.|OɘPEtɦV>y*m$h ruDhsa}7T ^yx㷢A嶼k]x I.>slGEW/=8/ƣ^/[\2ytlns,z?kq<(<)W$(:Mw lV K^BKi `} \Oo3 d{ʵE%)McKtu8~=`9#o{HK{x<@{ _zn5ݧsJZuZ#:g2x)*&(7A9}#?PgF?^hG:< o,^7Oƿ!=Sx)‡Kmf-3tܧ?l]n;rq9r?AA97cw{ygOgsy [kNYJyq% 9);s?s|m 1׉;u(WC;m]vxi"Ǵw(QM9"һ+q|CNǔQjP[m"uN7/xn8~;6i֛/o=j~S}_6wѿo={ր—9悿'grn-bmm#Ai,䁷lǬ b\# <>Tϕ q^ =֚5k>`:Syg_Zq-Nfk*_ ~]~v`%G썅%n{}l_ىH?Er)pԎf- p㏔ <ܼ~מ9"m6@ yGjdWW*/Zu/uԗdKvJʃG[|g~K?tjS}'ՠq8kOz˴Yq?S;$Xmxr2[8 khqSkpJK~h//{S7Q7K<}ޝS~XY3oCyEЋ zbBuwYƬኆV5//E6y!s~U% hִy(URV܋OEމlzR"OSho~x1 ܔWsx(/0$B!9UWIVyNl/Hgp}U?T%ȯDė\_镬6 v1gη2RiT"<}ٰn>.rr-k|.iMԸkǁ)gw"TH;@!wt>v)TYz]1KY ^'mk{5@R1Ԅ|Փ׍x5-:ߴK|w_66ߦ~ -'V˄{cջv6c42?T!=1XA#_|9 <!7"@ ]cpf, *OrkY`暴-S`WKfY ͼYh\6,ݧ-ݗ,Nj(4~xe.`Wfe+灪b.e sn+  9w0υyW|6g(9|?Ot䍑ƀxq>L CL*-#fx#4r~1lqx4V9c2i|#^OV 7M^W#U*&Ygog9d)1}FkEZ;.ߜg՜:n4?y=,'F?v3왟y>XOzr9orTiDk;$o'9Ijl߅|'qK6c$wߐr8MnK;i }cx9Mߑ4'8^CbPq>5 kmoB+2Dʦ0MQb2ld7$*ӭrQT <{1K{?Maoyx#x[Q_VR,nF`Z:PȒ>UMW͎NΧ1tkqZx5'{wPjw\*ُSVe-ꩱJ}P"Rxad0k 3>9ԧB̚5e_tF_]ʯw+M%I+>~ dFXY 3yIY g9$O3>n7BH' p~Ul| !൴k7^=K?=Ozs+-ۀLk)?ca&f.GuǺ|ǽNXyE<'dmgO6hv#asu6'ΠlG'=M  7cWbzzbw6{yG#L\֌0*ՏB쉕܎A$y] h՗1qτ;%FG?%H jm)`Y<grGӉϘ;2 81§gZ؎%9jd3IʹW%\xI^[9n_~!ǎ|q'!?͉4c`r#<8߁n&z.q$>uʗW}sC0٧j7Zks}ΝzO(mo*!{rw{,ڋdǶK+4A*O^62PjL{g"&rKSЛmͩ^@iS^Fz\Л)|u@vҩ#fٜ9tнu />EzS_D~=-`]WyN>UJ~,ƶ-޲#>YVo!?[jt _TJDs 1&to56QL~Ē{x%ܱ+z5K˴TEHu?X=H] s^?rR6x7HR%5x=uC+C w+Mʥǿܬ31FPBSTyOnv܉']ZugS^ãy/M^TڧYñKI!ԝv{\88pf)~Sdc?_őNBY_$/[|nqJՂ<'/^Xom{yGץ1HO٢h=t;%H~w1ĉYuv`uH{^ȣFCԞm˚rk'6甿3R!@6Z6r+[ TG(<> 9r׏i^T|wNgݡ-fW#~G`>ma}˗)ΘO5.|I0z5rz̏$"R+ `VD_"tZ{/} zXY!t $Ywp߅|l,yhy-ol^7W)+obR/N}>^mG jO8F^;tۧuH'Yی#gCBjwsč w>z_Mtl2ɤ[miP}|sύjz|05k(4 Ux+Jgg*{ӪK{x+NqXEw?1TOe ?&w7_p}˲\IјX<hoVi;k&vWs%PQ.Goߵ#|^ 3eL=>x]Ɍ*1L%w^pU{y_qڶ'=.؉y~#^93Vƚ{ˮ x3HQePԆ7/~K?͋QN LzmcjQVfOޢt H#0?.M>,䋉{S^{HX/6^J8֛R??G×{(徿;|3y= |z6ʩs{$ڂy|g& Q("35e}zR>y5ě,y8 ?hYǮ]uy骟ݯ;=H]WSɏ'/*=V2t|=w8py[DOTނgNr\H}Cxw}D/3C7[qƄp4^|+*ˇ})~}nٯLO,0X7BVy]OTDy|wĿ{;YյGuVYdi.BI^BqQ@Y(R%EDi"TAP ,EUT" tRe;ajKr_De_>҅w Iw_vja7+{*2UɕAJ çrԎ&?ilZY`oٝs~॰ Lc)#w9monۗtکx[()@}OM tz~x4xe.|ٵ]>gQNO=[y:MޗH\ ].uU^7+3o~ܦjhO_eJ2g[;Mrg|hzuZ>V@Y|nN wh goӀ w[ts\˭<4oj 8“wObٺ1ύu/Ni9rpg}i'sRd:tm9~gʘQgzmŔ5IQ=)*8WV p, Z+GqQx39V=4=pC3I_>Iڨ`9w+4Kʙc=^>> ʬi֪*['-G@n6ɽbufӔ Au$p #׀*0wUs;8QkWfS`9_i~z8QZ{q_ צIJ=R &^Q.wG=w[EN]y?Bw Q ߄|+׿b;vy}.LP@%w,t\OFkG[r}<Y[[ o\/4_+^/e׮?%ސ9')$A~~zj}4s֡'8s<y,x6r м4~NMLTT@ VdR+ Wvnp#=&: n2N)n?c/gWw +8o9J׍ I꣝aAizSIȽEqŃkw_&MW!dZ߻y=GU;Έ178O6C_f6>վgµw:pj'?+l$U?sN}GFOpXIQB< Es x7*0rԶ'i#Z+^_Sz n]#2 8';HFo w3u+>x8J9fÃ㿎pS h[8eG8`^ +߄4v+ைsTqӘ:p\g@deX9\eL6kG,mq76ߥ$P߹n(NݶN/py_d|Q3YGrIJHW^8bsE=l6P}oWߘgpujBm42-˽x 7ӀM7Gsǧ~^Y:(k3\S|-kqg>;53xJyD-\#=+i%ܷ{ysO>*O&^n݁{ӈ{BqpXZt'޾ǿ0y]Q 7˳R=ot№]Ewq۽ygpW|JT+ !q]p) ]hPQ{d)]/(<_ ltUhW~DI]BlGU p&+:RdR֓ޭI.OI.g'WQt[ۢҙʭ>Zxڻ4oQa=gym]7juRx‹N04c|yʀ!w$48΃w4wȳZ)VXO?++[8lgbG"?ٙgTu^06dr񮤸:R vL9hjh: ~t(x))w"}p2 /o2CTlz\\Ouu:O[צiʮ^[Wviyw\O"r*˓yg߾qc?O;OKŝ]{ۺྛp4N4d e߳yLԿl=i =IgG7p Ksi.EK$?L'/]-) .ҔWkC/Pb3C.b!3M=3Q{&nvH/ۻ:]$\9K)!ȵp~k-O?ua,m6.rM {8v^~|i!3=suQe9NGO;bo<]d_3vpj?A|{3 &Y(kB QDȮآ"1TN,ZGiE"Q;}=N?{nekHB7'_%Eo~eo?%zN\(s 7AO~l`yWDh>oo0j4pq޲g%d*<̟ܲ'{'P]%˳î7 ?ҫ-[2,Y>#&w&8z^wdwbn\${Mz*CP|Ǫ7NGqdu<Kxu o?Bsy{͡4һy?z^Y|슝3Ui4_t"A|}Et~|3U7Hͪ!kXXׂ|_'[>O/&H,/?g\KcȗG)w@"Jݰo{r .eX5a-^4ȸFQQY46ߋb^ W(mP1Z 1rkv6ri;{i٫X~lV?5}l Ou:x vUX:Z”X.|y,z@u"1N@45vsv6v{wO.[5hYySvv3%E|+wq1A I& 9^ oAG gvxM2Şr&@.Mk!)!?B^y{qc$ߨ~M~CY/Nzu#;)(MnjyG_'wwtRu ..z5uÖ,0 (˱>FpZc1eq{P=^xPp)7噒oO Q]r=-[5cPaӍ*ȓI~< N'̧ n5U9.ft|}z$c \Sn\=(ğ^=Ʋ?kol,5K?C~O[ Վf..[MfLԙ}IignzhOؠ>IaguwAݨWz Iz* ͪ^SaK{/WTVe7NyEh _ܿs+leNJ;GO wk?1_)>m~E [gNJM7TC}B9w#TCr=4x27ڛoS3QPQCb ]fM&-~=Z<ԗ;ח@7v?}-)o5|F?ğ|{uR~m Z$^הwOQ~qɾh7UkX)\ɘ?%) v@"^>_иL}+߸'=' q)K8~{/}$;S;)~w!yqIkRZOY.3~ŻHz8Krt~ܭ(-,Ѳ=E{YL)D1 $_ʷ^Sq_eoʜvNhg ޘ҄3vyv3NpKkg3l"v[:ҵ]#;*҇N_${1;;!?f /iYoM K|KUIk'S'P}r:`_|_dp٩i; iJ!9"BgR!'.XoOp'9C YR P$C?RK5/zRf[Ɵ.9] >\LL2:Sl)O:!J7{֔Kx_ٓl-g}ɘӭ> \*CkG{(2CZ8~\K<~W&ū9GJ|(ˡ [3 6RӥsP=]w3~AnX2|}WHL.?'*VsnؾTǰz_ވ6.?H_W]٫ڎs֪6luf%߁{ KOuzI3 L|Nք{9Y#j=[IR^-wc~|-QqVXjdo>g$7zWvn ߼Lc ~]hM9o/Owc}^wxŁklHvv{KYB/{a>s0Zk{OU`ٗ}cSlhτJ8g*t}IS!lŸ;(x9Jk~A\K4zؔ놝`Gߪ~W?֢CKT]N>Dq|L>%^\Y-Lw ䷐/ufW*F+ *W2JGhHY;g?JSU*A?7o rMh+xAi[3U!`}ߊ ۳5ʫº]+Tz]$opfZp}\OW"Wx>T/!j5Tx- o5)9;~p:Sy.!'[Mmn3giQp[x&!<= y 5ˠoGB?*~ v^wܯhcmu]nNk)]ǭB|My:._ x ,'cd~^!d.*O:Y^_j(,۽/~:|kװ+~:-rxK\sg*?sA@|߅>#?4=߻T;ʳ"7zӃIoZyNJ?obg%;<3/Ct?Cpᅬiyy&wF>?#R*) 뢰ĕvb1Տ9+П}a)MbǎW[g3'<ϱ) `5E{~i-3x\kx'y&gRzY Y=.ʛ}_~x&Hc#[Hzͻ?zKy> 2n|Q q]w6>gOv/ٗ{wJyk̷5C!p-O[0OG|oZ>%߫9;&|Uy )I? &o2|3ɟVX>Ok+bc>֐" ěKt$O Z:Ϙdc/Hᛴ'y>;˿ۗ D?KW+)Dή) ϗQ9ۺ>=lT+pg]]\]c쳃xuu&8inq9+G=;_y +Qv~ԘAwɏQ'oI}Q#G/Vn"i6d+2oC /$uX-u󏀟J?Nn1[K} SW㯌C3.ЫMzO po`L^̖tTV9WWťֻЯGl 뺽4#~8<0893wf#5VZCM;^S֚wi¸vs?|2;\~W=ᵋos)5^[UO Oh Ozy.@Qr>gY!TfYk8cOO%뮆n!gTa4=e 'Y^e_^Ώ)RGu/v '[^U}$<>MnD/#Jm^W'ER)(& Srsžԩq 3v=}s>Wؙ/bOO&Bſ1x?ci޸'}XklIW0(iL Jvrx"zz_<^70`xSfRįrt[Ao=w:|6n~_AFӁŽ]븙6S ^(lg\"4L8'P浟i1Z5ܢoNxdP;>.8[}ǂx8 ֙gq0bT6[#slѫeO4}j~H;to'݊x9d`Bo{y:r-`5x_q_7_=!_{(i3p?O:9w !|NUS \Ӊ A*R}c?oXVՊ{ 677Q NDk7ً27}.*s nR0m4'g<65.׸3d0۠p^<֘?l~!!g]"gn<48d>q?u־+5]j?C6 'KƯ\Epȳ7>'f288Y`\˞7kٵæG_0>n!U߷A/Q6eʚ3U* WQ}S/kJGw/?}:K)U290IZs;>lJ,Q>fd巣, ^qݚ}ئ9owE͌;hx_}~^ߍT#>G 8ة08'XwTNk {Iy?CC1O'سbpU=}y6VYD?[կ(;NϦ=ߧЯIMKϷ1lơH;xKH{}‘1+J6^;^=o=kEs; k@c̑[cj*[׈lKwu2=kΣaDZw*jHJfXfw~Rׁ³8{.۟U|%j.7S.?9b7d^Mܶ"{6Cg 9ҟ^3ciq0򱑭~B[i.n܎72άǽ< g庵p\pdo2xy)XĎ,) {mXx;; G^ORV ]nLYYCn^u4l죴|Y񿐙0ψ78;͂n4܍2?ޘ_lD] 6+ >F8[M4`[Q#qgQ^W.<_WiLmKG!.ݍٶ[ޏZVXljCNGI54+2huy=x'~Qg1E3#)xOXyХSJh4G^7<Îi=79ЧHΏ%< *h?M3?.oO=mGy{(9cњa31ў븙_֙eetf=|N0K|z}v$ qh-Ni/L>`eqL)EJOzIn!`>(~33m]e#[Tg3G -okvOtum|`@V;_/φ0r灪\#CŰI8֤?oWNgomfS4$ng^3C~}~6c}NWCE7~T\NiLQ .+ا7?Źr}~5\xqa8"[7^L-*ُ:{G$rq:8֠yױyn>0:\^):!,p:sK82deNAoEߧ0wT8)=]{2k:B^}O>iKyM T$fB^Y2X!JW/5Q|[/;}.u'"dˮ.]5d*FauڜT;v*, D?N70 sYЯO+O{Iy%!oDz)|-3x;Gنpfu5DoA6|i PxKޠ.(|^<xy\T^IhWǁER-'HȻPvJaޓ~?Cz?unc.}s'|cTیQ~~o <0ũ\/y_O߯AޟK\7ϟ^~w8!y+ۑ{X|y:O/ST֨p3~eJH񶔰_P=<=ژ2'yuY䣈0WeX2x2 e `^io W]~1% {_ Kii]'595 JyCD|qtGi/]{Ag{~ 77UݳU5_78y G,6CwvmxV[ˊP9,o>J_5[/|<4o bO%Fssl7(9C8 U)dw%;;j]4^@{JwDvf(η :>ø{}Wa&N-&Oc1އ>{'8Lp] 6qp3o&~9bO|گU_pnܽek* ͌C#[Mre |`u&>*Lk[Ƈ&RzS$|l&b^.Ϗ{4sqZ~\Vc`úu]IaiTeCC ޟۣ_<Y[Ma~; l[>u=8↸2F'<+OG޲ /˟yMquV1혗>7 ^` AȳlJ*= |#;CG~(c>O> xy/5J=LUΕ=wԱWx~Sٲ4g5x帽w.^\>n]%MYUP+vS^.˓m >+s\3â%OA~D?k'/g*B%~3H?4+&i^qlRqZ]B..cKajK+HG{3^>ͭtrxMv3Z$;;oFCkٟ'p1Gi뼐?g"_K׆xbޛ,ciL#کS9{#r~+gBމ+Lhׇ3J־qOD>yoޗ:b"F{}1s%)GкItύH, W7c er߃pwia?@ۤƈ%$jྒྷ&=K^YGy&`vęE9:ΰay{6nOo~|+A"k9Ks!pm$ r*==G]J2!>Oǒ"eCƸ&wH4[ tRݗs} $w#¼ӫ\d 0*0.O'_c&[Ͽ~z_l<>7MbΗy&vBU[kA.JefpxedױKL~<_ ip`~o8pqe 3OrɭgXIU \Zhǒ.}֯OS~ۭ"6ڳ/rtbd,/r7mV:|>%[2~v>vq|Z/cN]6 f&^.kv>7|Nbٮ.&(kg%],?1fYl7@7Yv)vA*e6}$ߏg({wRpw*\Ya9#U_<(]*>NrbYBBsn5fxys|[o_K7YϮ,;>Hѯ?G_ֺe{TJ/˿!k.: փG?ah4~Dߩ\Zp%Ն/$Ŕ~5dkr;Idgׯ v#)=m}ڍƝgg^/ 9碎iRlRn|MٞdDb:א+X|\7'Y|3 8z0ύE딿m*u3;z/6I4;R?)7Uuy țl ).&7qn!pa{~ݦt,p|lAţ:v?o2Ϝ^;1i!֙˻061]+wPc[k-78 zH:0_;kΞRX||&OTHn/l.\N/k{Vȵeu/ǓBbIq>2^{_~sܼ vݤݷ . {uІCܺl=Y~oYۈf\Y|ow,rqcN:d˾-ȴi}cݾeu9M拏ŸPoN{Ĺiob-ov|{oo/ƫՙ|>*_]ŨĬ; ;u9uuϹ_V1 :#1w'Gd}G>czYL82k>c[b'/uu/=o}rp`GAo"-ަ9;7kg~o+szi;ٖ8IK|7I]k2u2;Gd"|ѿ gI[=5>x_/{zoz}z;ʫ+ AO0)㱯jxK6(|ؗm3xoyqk?ph*-o~(R&^RkEH;xc/_=tv;=[`?)}B?Yb- <7In693]~Bo(*;Zg~PK$;yL.?u>[Xy>rezz5AtӐOr34o sv>х w>SKY'k<˫%E$_ x3]J^ n_ȻsRx xpӥ MfRW^{Y\kDwkNLvχې1H Vg<ՙSocC^8SCVzDnt[apsZf&s/8_-v6/+}뢲*^87)O+x,o1;o2CgέD8tnj1q=o{/?Lum&}{aơBy^L.U]+L#&L 格^Cl&%,9%~k~C?9 -lŜJ=JqLCٰOko"΢{5?gSt}3`_Iy>zq9l\l_;9%]k{z)ȏsF~T%[o`e[ORѿdX.6˻WΦ̿ /]8YsK}ܱݹ>7gI>{}Vx^#vj3?t1'~Sm3ԥ8[{\x0dp;=2vjnN~^[;Rbn{u=uDwиIG \vt~Gnn貣㵝9;龜/.9i oq-lyVW´ TaO!)L{I,6Ÿ %aڻng}a;#i[۾4Qkޝtzn:KDWu%*2ͧ ~^#)Ѝu ~6*$N;k0oF;Qm8D> bㅝ=N>=s*ʬ;II $] o]?3{u{gdi9K;.5{)~DVo@Y{:?Mx+N5Qp,1fT!ui b E9%p?X./B;M4߄zߚ?X3{RbgkA{I1; |Ro2Og1D0= s{~\f(q0ݽha [GXjp_A$+9~>1܃\.*p%7 r\eM+pm{ 6 ɗG _ƀ5#.Gu#.#3.T7-iRqiCu_)ep6x&DwS‡5k?/frFvPڙRwjx p,}'W ?G_! nP9Xx>.grg*C8*kW9*p.+Q+Z&'r(ss#pͨtL;"pU=>+DC[D[#uģw Si/82ofMd `𯽡y}.bJx7x8*M}\~~Gl~NgsY}Ym3k'<  RŸ?&vր$|5x\5_I*Y թ&4ux(L::Nd_8Qe^#/Y5!ou`Z 8i߁=LUKc3U_?Mpka@.Lc\߶`57L{5N{%,.4sO ?n=dg68y]>LqCh[DŽǀ >)|c 4f~@.sPrF.gpu Iqx͍38wh#-(Z*24.c2Hx8GQ2w"8Lszo_p_@UW왟tkwS̻(\pm N-# %Rb n-<^x2{!}_Afm׃~>X=,> n*0p.8 }v)kUKF1r怳qg/>-wV˹%>Vpr{$||Oٽw9_wmUF Q60/LX^K+X2x W{ZJX3Vriۑr|,`"p<&gwGZKl5HzƆ$[.F[WX_Jpka߷s?e{']a!pjW ډ|'GnwX@k f x lb*-g+9cd#x*^%$MUH@8ПaHLZXSnv0Af%JĒLba+%ބ2KDRbND4 \ (pă3sw@(o'Eրf?OwKbg(d(~٧t $B{x!n<>̈'pFB!d*)&"*3W,/ )0Lg9G1AIumQRt ,"BB̏3__X#pŠ)_"R#n*Ox[h*lL*VN`~0OH@9B<ZEɰ,*O?q81ׄ/a hNJ} [*I57ZDF˛ F?Ro"B>ӎ@=HMƑi+ F(p$V 11Lq![b4k_HFY\خnA,6*Ϯ H(/UE/-2>?ǨHqaIPDZ,̨C #$BqsgvJ؊'(dG-é4(@{ 0 D #$(I; qKe[%KiٲDo-BAhЛة eFR(9v _HMu%8H`DtwQ*]vyéPh% ٴH?@+E`1+n%ԻttV0/JagFmE}ɐrX[C%ꖿ" FcC{O{ʏEfZaaoUBRV1<*?8BŬSe8I].I<孽27zȶ:J̯?x_%9tHQe9812P\"ߡ8nEW=#k6A i2(pTU" N?.ݯ%%wK#5Fk+^b/OOGi2B|G,_M1/--b*evLkO!z/)O\%Y^c4>Pږq ? "/ǒzNek~\t(QAw2s1I_9;G6g8##)Nخ`SQ| g çdti&c:=/f|r@WM1 {%"_K&e)Djt_ز`4BW~Qv\ &lXezZ8IC` eHܹ<t>yviyɍk%]/A^:*qW}ª`$W9,EcUۋSk"lFw= ?BՄ!^`[m,4DJI.b/U'CyNωMF9؃u NB&z״Tf*A(33SD2=N.*36: d"'RGpDHHVpAڨ%b) g*wByQ(#,&9I4Q `~}鲒;u!GAz,G+5E j JR]> &L&|Z$VfHe[C{d[TGt+߹Zϋl%AQ1(~gFSl#@Y)l'[A s~H NWuG!4vi.9Z&BudO2dcEu/9`Gz™&xaZ˿;eR]>ɿ?_-ZErS9"9 /lYv|뜫DK),flۧLjZ;er5Ȅz.k)d}'ݏ̴x,Ҥ6رG4m+ ܎՜έ Mև2.UJn0;:]T,tn"(ak\hgbA A8v 4~ W՚ܭ94 ۶ eR_-PNR!}b\N@r"0ff+"8)u,a2'1r{))LF2oo FReSzqXG Rv*[( Y*Tl7;6O&vig lVp)drȼ9w8 Z}o/QR]5ޠFm!4ӝTr۩4'w9ߦ?ZNSΏIoVip5V>_^ZZڣXS֤C!nԛGJ[h>jz_526<.ۼN{2B e*"!_$-W&y'}^Sך&p2g)7B`ˎ',=@Ŏ#9!Q\/`N 9&zͳKB4 "gYqYۙplNj,<@<(|&~ID%/9gYw-O{!bc=yJ##d4g7H-x$5'ODBxC:O? !ִG9ul#񄃩iiG,KB R ҸL@Y'&<ņ}% 95e;Do5W)YCˡ0&2V2l+3BvTLcSYs]H0K2>MdMC %)O| XQጋ@I;‹KcN[+۸}pL kYRtߝ̈́nokfk]QO1E١8{%' wfW:f҉lG-ru$b8(@S)?lshDK._;R֯7TRZ@ЧbgwB9*ŒA`I?h&n*ܨ..WߤYC?rr8Sh5-~.͵xU99lLve 1x) `~1!0 ҃OV}$?: 'GfQX c=9Z/!h{\J/l$0ҁ9RF^y"!_=*qmJyOHf%!2XLLcP:\zj8Mw.*yq,);Y 9꒠X |j]R\-aR]rl&ޠ;!Ӥ|8؞)ܪ99rdwWJ KGxz孀Lo}Rv~g;C; HPxu4[w7+QpoCm!޺mpv[f](l_xY›RE,R%'{;Bz$=/B^D^ QG7BanB]\{U3:q&W6Rn%W!zݭNɋߤ)]z&xwkc@pJ1햺P.iP˒kFlXdUYJf\oNv$YeR:&h{l 2]K-.HyDD*=McMruH(Ol%EBW@2]~~èe\μ"YV iτГUn4O${!t;N(&AܙЯlyjHZF r[RջXNӮBfvcVqϋ"{",6ܚ4 1H8Ɂ$RŐah89DKۇUq 1ekq,=Enk Y߸]f'Pœ}q= lr"Sf'V!+|!AW[W.һmG.窭Te_3n^Aקmp u'+ PM{W\IZWRW;fWܩ ;vOM nqtDodWt EA7Cצ=,dn#N88< Ŝ0A Òv4!tKQzIAqB'Av`Skk7^Ua F*/u +fw%լ9{i㮒F˄!Pi >!C)G},6DnF»Grbo,FcJ6TkjGbqXRkhaYD ՛M:+E&%\Mtn!YP2G*pE RB~9i9ΎyG= ܜL:\9*'V˘&.y&pG{Ľ8\ϩnu &݌ CvӪ fu[z<65 >\a*"e~Y:Ύr㋅pce 9KP[xw~F\X9##=Uǐq7dڤE7ЪR#]F3J5>W25ϕ\9ub0ag+%#$)«C.ZhqD[ӺksFH8,U!y4p"i.&9l)_`::4Evqɦp)=ymn8dۄҹ)yE1腌ʮLS(mrXƽ8LVΘ%}kh^o1T7|k[٦Z NkG+LY3\)$DYHp뼸¹_g#\%ͯ@:u;$!DΞ hpW&w#jnB1vJ:|-(P 9QI*KFr6n'½&q*G<ڰIxT8#ao?-eWn$[=TO@y@ ?Fv5bN! 4N OVhz H& .A{:WpY.=q: W䠲Fj`3uܥ@t!(Q )M̴=v-T E^#ij"Sۢ0)Z,LAlW6ڂz~FsTyC!Y.F2^,FƷV賤dPVKɯMbTX+N{S5FNД^X T Z99+P*~eEa# wC{k δï/"%-I52ޓ(c cs^X3ReJ'0eULb(LjLy~%v(HڡxcBʯh0( cީ"㹖 ;9lrTEf,|o#V'6 >cVV:zuQadKl/Uy&zhOth*՜+ļ *mnT7BIbcB2]мbJ-;a T?g%g#m+f%w!\8ٮv@AADЉƤj0HVpf(r=Lkbϡy9|Jԋg]*eY*obMV L@FWˠ5YIPwT^DN]K\,ƴ]v\.;-t(A 4* U_Hz ?Y/47j:D*  UYHgz߰e .'$A^ yS`f<| ⡯M0Jyr^} PsX,j|^f}6N{lLznALWvZmmئ|֚~* M8Lǭ (vؾ~BۯL>tsj܄jb¢I)l2bG9$K)$[]E>70|P⎻Nx E~e>a?qgO/Iـ+ Mlyp/{=\sę3n/zN%)dFٳ6B㑶Qyl<6|]_oM/8HP+'ݬU=r"2kF&l\}Mi\ {7ٟr,w;h`8*Е&ipzC7մLӋ8DLHFdp(G 2 6okUn^ds pV9cY]켛M6-fG>z41vO/!%9ڋ3O{0",FG(gWΪcvfyZj^P!ܫWnq?ݙxa5%PTJ^?T*A7R*WB0X֔1ZV.}]tEK̸Hf}))xJG୙!NlkrtzP"Ъy82t᝖{J-Q.>Kl^'Dp/@"'΅#2WwUJ-< *K*mT!o1ȑW=ޘ*PV0=_ѡ5ܧNj6tS h}9y-sx|ڻ "juW5?Ý  3H-Z,~ժ$D ]`CKB 7 z>7iwRl07&7Vѳd2zẆӧ֤MzX~V邒^mUm8VKśqܕKţDɕ A=(hwe-سa՞clb*wqw2h YWgjIԝޢȉy8.L]@.hRBpݾ]=Me.3iSV'l[Ӛ\zНK7 !}]H<_6IV[u#}eGIh$+=4Y]JUl}.TY&pRg6 i攮qR' wmc{_oM {O *z'R%y[%g$>" ~CNߘ0U ZR|:_3 J$,kȒYO$U?9' i#7'ۍI+eB+4̨$+:T2l}j KHcUP]qe1Orp: C#bG;eˇB (9 *<ӛGY{riW_5jc=cIz*-Z"*B!( ^9]`ՋwUӷ ?h0LKF*TXD==^4ZecM!vTi#5clt8II. ^BL8'*^,::5xA<:CG4J-!_Tu Pe< vU O` .$g>n&(4ӤXnԱUYwȠ\hUBz 5d $M +K\2U>vXEU\X}9UV1t@S6GEN3V'sYz. u(Jg҄#,͢ZS ŊVb*~C8XA9wQ+zr7I ݔg?gߥDU3ѻ5#Τ:M?'ڌ<鰂g[.Tn5A]\RYA}T,;PU,!a@zg;l[<`z[抶$êV7@sMSM+=5/yPхQzTcRȫ!Ӏҋ{֥Pqe9_m\I*4\~WsW2km`O[I3M[|oUn\YCY|s y1 e+h@nl};vPWm&:6s[5H3昧&4m;]ABwug _, 4_q,/ۍ1C=/XFBqAq{uaJsF IP>I(ejfԽۏ|Ik buFPC‘G_)w-x>r dS(Q¡.Y5i4t; GMJ-O,NEH*BZ:.Hm=BS8V0 r + Kgh9PY=֙_@FB ~/}Zɠ$ 633 +PpvGSa>O'Gr@|51[-d>=NyC{4a/WB/XJ^+ _J >^=g_VAS\ aQhPh7s*?xʹiIJϫصA1҈!uSƽF{l\գ#YCc&p[g)P˜:bwQZd#Po &+,gE_9ERl;IfJ1FX@p0A8,a>0ߦHVVUsi47Kθ Co"J_f^)Z15̜(UR.v5Aڻ`ć y$S^vUӸں4jg&P]/8rtS KB5z)+8ti(,顶vOSHR5$z)?HpBi6/}.Nk2Ֆyik"Q};:k`y儔V=TN]iZZ4Y'F8l:(ڷZР"9A.GS99&ņ22)I Edj^/R5 GSyϲȝ•o=?E.Ԋڝv& +abqYy@g/psnKc-v(PQqbF*̌\ Nߐ"*xKK^+?/e?ǽߢ|~L ƭG{^O76ojNIwvC\p*ެ$~܃TT2vԵFλsV:Q\>h0QTj +\mav;0^/l3P*uiUvs`ZM)/CT>uM@ǕL'|AՔ/7{);YS&T4֮$|ۦH6'"/[8tkd)qG3ܝ!rD)vԤ@(\NzubJng{RݥXKoFV"'B /3'DƦ_ @ȯkMv~AiiѲ\su%ZkNKFK˨2E##8s1b!FhRتOT4GYYnԔN@pmXB,:cNFک&e^΢"a' l/qЋVC>.8I,FE=Hs)ӂtÅ4xKs%&'»I#oE z:h)8@IZ-DfΌ{XJIdaժ/= o/e $]?it'dאP[Zd=/hI8.NlJ9ٚ_Kc,׎Kvz>4|[^?<GgDh Ox"lJ !5{qaK_4e ÂS5_uhBklh?3TkIe(pˊBѲL#x 5'д֊\/SyѽJ| J_%t:ii3{uoEI/q G|Z7!;%B-J& S)?D;"x L!MtA&~TriC`C*^9>˜S?]9u!r r4~ VP$u:Wmn:u<"U_a*8=oL@PYAuUM*];NUi*; ⻪4ẚCd/)x}8aΕChX!@2gerD~I%ՔV7*Z ?9&3u@!=BFJ 6ZɱE:KV&U' @5=:V>B#%ĎjeA(JwqD  jkl8<=T FyB9ȁ{i8PVp+|0P]v ;N(؋ AI)KW7 `0B @oU5*40@fl۸2~Z3KDئJU9AڂN:2qL'CzT07{@E𗱙Bn,֠B6% r ,`MzApR{Dfq}ѳt J6A|[ӣ0D{I EQP٪h~ c{RcrRxl=>am.b`h~UT^"Ҥ9j^wǕc@"addJJEO؜9?6DR^6sT^`IHEQ 2s4)tJ;]YdhFI-Vgo/34HvJz ZC_4f) 'zՂr#mXs rа>/]7C'CVff#"3c~qkw&_M߅]@͌GsS 7J n]ֺrt5h2*{>6φR%P*OʹڣμuHi^s3)bZ0n x6Wl4݇Y3D sK u&\)U{^jP|7mwa}#NYӱhxh*;c T ԕw }|ì73Db/՚ ~ӞNq^4!wS3{JqLWǺuTj*!.!D'E]yPSq{Xyk=vt#x`.Wz=?vf(Ԟ䐁vUD%1|r@e2p>hJ[0i 5XENCt_5̎IGh{>t+r=zlX1hпzLTHLC՞׿I\_QXǹ޼z2׻ng7? |hs}hW4 fSY>Ɲ_F$}Ʒ{Ymsq9HJZ =Ȕc[_4xPQfxsSoǗyԸuYmY_PnX7v߻a0qv}O?z7JLzS߽fZbzߋ{ZeEDݨ_ef{]/A*Oou֍}hܮcMvd>˥" SX6=AWdyNiRW,?|EePQ(آbQOgxmUS 8F-32K]?ўDC%Y cvڌpdxҞc]'a|] k,8+{YtuJ蛉ϫ!|6ypnR`uNK {K_b}Nj$%='F{l_AMv`dW>> a[l׻y7^d1&;79.ÅO>O^څRݎ]`d-߫asTx52bqXD1 FH#+Bc^cDu}ր6k{6|%A; 0iހz©y *T=4t56Zg5|{(x+p9(U)oG砀Iht왴pO+MUzԳ#vB"?.NF@;`yS^{qS 0pBtGiZб2WǕ F9QL ,OeIhe^t06disbq΃FiܖtwH:{{O /ՒfѠ5cg<״|[bu6AG7>tbFLIRDFBdw \}(8eyA*?Hbv\~Nc4i;Ajޤ;vpWUE(k[ڳ V>7|?Tm~llʹFׇ^uMe5H:F74l k?hÌ)+묾TK$; V}m^5F|=x ^0үeFf@1j@ځ9Ѩψv;Ty`XCNZ?nLU?\w}[z^oҎo]¯Zb =eh |Bͯf.Kk.2j/m^]V s\M>IrAM^Iw:wQUH *3Mdj벚;ڕm;}: _bhױ__5vؽ뭌/'n1k=uRV0qku7؝3[Ś8@< ~y#;O:J%t4akн2Od%SrȀ\Iц}ـ.,웩|]}(_k& SIS?81b'JdþVfľU"(Q}i &YM2z2);v:.W?_vϋA ̣!@FA +u[/k-l,`Yl'iZ{sPjQGq/g;@D)lW#\X0?^ۓY.Țz_ W\2?3eu!O_o+.׺!vM}YH QB}֞ќ4\Uh~EkC1܄KT+&Bv&l<ќRGu^}:װҩ癁+S( ~ R>O雬SL%6<'_=jMTC¯Gxk*JqkpFeGmoYq˾:tѣzޢ&GrW?~?]A;Lu# 2L3խj6ҮK6K89|nF s*TЫo/ [ӂZoV6m@zJ^} Uahݱ/6q]"o\s]%B66 mO|.Ar9GhH v!+qwuz!F#@?L,'~4֝x=o>} +Gq\fBr.#>`[`J:Q3gAVr|ÙQ`(@<Ylb`b=8u\(HJg^apq)m 5t/,hKL,ՈiR5a(iLҨ4CHE:,!xre vx,Ba$ FKh #{j*cjkZCj3m|Z=G~!#@@KcL+ʇq1iD#6%@~a;t0xm, zOM4{R FiXcfx!I91wV vGVك Pw"9|nKU"~X6 +7:ܤ e0ρ`Llz],vD eKP*"kz5NEh 6g(Lf!C2K,!F~n(ʣ_QR'ճ_dյck w@v0LuH/јr0s 97xa3o!0Y7rτx6E5|6o4-5Ƒ㞋,Ξ)Ԯ]Cvaar2ۑ pN},[c)5spWTyƜyBܑAD2Oa : NFxcI+8y~o:|{&F&vF3IkT˒݇#['i3`56).x''qlqީͦnVF5,7͐_O0lߺA:Tє#Oڣ Lg#uQ[yBPʚtk ӝڳ[_'=yeHZѰBcc2>[UTGW]+GV>kS9ԔiiIùՁkBCtLߚrh2ڐa}֐3“b}0ʂrj Y9t>OhW4>J!}pG&I.VʍuX4 m Wyٔ{)kvSv0uZʭul2R:vB>Br>ŧS>I>\?/AA~ē.٧?|*xHe*؅o5@*\^Y; luKKx< NTJyw@ԟYS< S$#W4%$'tR+'zj'GxsarԠL-gaK]3ayu-ݠSQt;Js!0yt )'}sƟxl 94 ߟdJUq MG*9^J*z3JP_s@ZӑW;ksGfݓG`|0]E>ymA2֐wErL-܄4v;Rc޹_: [`H0nv>B<׫ _c'=zxF* Bw7`TiuV]GWr atlպ~n Kҳ22%H4%PO:My8X~" PfX>bo쓧Posn]UTn3oμ^ 9OLPTT+߅jk1RwBQs3H F: <UMi*DhJEj~L5e9T B+~$<=b%{V8xכDAyH47:Mudө:>C![\j _ݠM (%of8Ĕ7l\yЇR1|SW_=iM`n Z VrЏp ?UGpOKMf=3PˑCekk7b؇Fl{>Etz&>4 "b~Ktpׁ[Y䪪ykWUCi٤(\ȒYj,WhMo|.w::6j8X;x4{Us1leU~cnm7 +hA:qCi Ͱu[ˀ.4p"~CqZpt{F pi+h׀KG6 -ZR&}].+#3q#7"bאjY^եzSyV왪羨prLְ`n|9Ef׬i.=-v4ĿƷ~_TfĢ3Z rU糐NC Pxj`*%;Ca|' *ۀiK7i0$Ӫ؛hzsT&3m^pid*tէ6|ŀbT|9/~|j޵=N'Tgag_~Z WkX(]Q5:U4SYb-Q<]\b%^}ӹ 6'[ļŻI^վPՌ(6x+aZOV\2Ou.-2&4ԔCGLu$;A<0uン|,fisd5ݧ>\{.!aA9ҕe'筊 ^+TU2؅BlDh̐KM}@I*r>B(JMN}:/̾$!zG82q, /h+%ilI=[KV{ .qhVj ^[E@$ ~ OtwރAV+Ai^;EMh>VaxA8yC QP* 2F#`cF(NG`LW*9%rVTX$ &!A 0[PEjڢw𪫗 B4vc Vήi<#{ fjԞun;n{vU>5!BWb9lC[+Nkz$[΄:~6#an ̍oяx2^3ἡ;i5\}-|c`3? NCѷ$*azU9hCٜz9<1ne[VawHK8̄F=4]%qO<ݩ 7tNڲ'NOwO+Ĥ;z|^KFd0F!hhАNlS]u Q\5gB }`éppLސp2]_+k}:~_k}mjכm"$FHO7㒆 f7nMWlhJv?ӷǵ #KX Iw6i {d8>-w^ri~#NA!j%52h:!;nDt-*U}Wׄovۓuţ>B{|g?'lvZ ߄Ex2t.nc9ALJXY6Oެϱq|*yt1xjtrGKr5 E5\ƫYza|ҹF6ډ%&79T ;i5&5Topۃ|+Ԣ=۫.QpY#5sF7rj-x:u/}DߘM91˝ X̺#n2OptvjhogGד{*K7O'8Up$'H8[g9S5ͲnF"Oji`e|BlDUz*ߓ>Az< 4HxYj˹{V썛6%]dg? l߽}[7R tМ)B 7 _Ŷȴ|.lo4?a`sO~yO3 ~>57oA83|&LΔhujl6 Gk8 [ƪqS#5}Ezh-7?$rɦ|4Ƣv_u ?oe=XwҾcK߫NWSy0/ӹiLx:t}Kcۣ34i>^cp`VČL!Xm|Q'Sy,uXuJH;̾ޠ)Aۻuh0[ W9q[= | A`zU<'; e]~+lDwÚ{Ak~j :~t6o> @CyOcㆌ;5~nSWv7F c@\v@bGC󦌦W=ܘcjs-~ 䔷gx09?p"Ngiu Rk;i 7\}]osum9\Ml0p_d sѷFtgn>`UfҕS<إg7H6quLp/_ˀG^aN>q s*2wjZkdN;+{oDSA5TWHĐD~<]!&sA`ّfe.#Fފx0ʟq宆=6ʻK;zw.-iz *%ڪl~QOYN|E\B U1~r}zyߗt \Qjᯕ©x|-[jْ*cDҬ+՘Hx~̦2)B"^4кԪ`u5*^cg W;uV仡f2ERt! Onha(:46/mz㲌jdf\aFWy~)geEA`g(jhjb!T^x*-ʸTsVbVY%x y]%45uG'fn^!\CIWf/-_['Kx^PйYrʤ1Л7HH8GI6,J$sHhl'EqDB{ŧכ}eٷ(.`0˞^~_Tś;@1nd/yq:h:Ri[yv^ *a F(͏VD:J(^R;ҡm% 9y?A%)NGFE]YX75H2/Z VrOK'Yv,ARkԏYz^nHR:V98]g,/)i {Î~ ,.oA(~vIu:@(_!N#Vbuq ub`'Zu2ÇzFP*ELj<ߏ@a6 NgL7W¯k„ =Uv -liA3\1fIyC ᆹUWhהPV0%-8GMZ_4QBٞO wʄj05 qpxLYWhD2 ܩI0\&nmNaJ#BpZd{Jq&\c<6yI'>[PIs_KV:7O?-+DY}Wz=tٸ) "G4b]ř\ytXRy ՜8u6ŷ"#WZJʎ5V™[ 'N2G@Jwũ Wzㆬ:Km>c7GNx#7s\jݺgD` 0Po'y2Tnx6^}B 8Qrk)CW&ҘuU.r̙K|_rZ`]|^]8^Ԇ.g:pY KmmabQ'H2=ù\$A#'sC^~g/z.Fdbu):Aqld]e_RaGNfX4^!~V9B1z@9C a6s1{akgzg;sw/s+fj* ͜2wnqSƵ|mk7C+7p 5n,h~q56y59nN!WE*—̥RBD#UGi!kKIiʏuq:NQuтbRC|tqKTg7lV`V|u%攵cJVYaKu<ەlW!(Qdq韛TMf`IJ).W<_@YqqGiY#Ŕ-,ak |s0;]bףTߊ0KYo#\>Bh*/QI7 TH?j@7ZktvUp>x!$j1nۃ(S_<К}Z u=x?uc_ˢ6Z\+\h<(Lv }+)ιB"gnb[9㛚}WQSB5,U4|UrzØj(U42-xniwzԛ!@Q)UasdzVv-WWa$J==|姽֙(0 il,uUR ˃tq 5mRO.m&1zCxԤ; `|PpL۳SqT:WX(6gprղOYgæL$S5w[mQnE pH'N.q'琅"U:<>+[OѲV2l{(C@kv}45e_FmopEyٺf:950h Dzl5;8nikWӚ6* %{_FBF?T`z2"~6Tvאr!UG g3Sr`j.f, N| ~A!$~jg{[[*eQ) '/tԧĭfkw^E?6kae-7l^.yp'>K <@•a7Ye(YUd/a 8$lpJ/NC+{7v۳$Ș>/|!*hof]#HηDn=2[|Iwl=Nޔ& z"p|a7o5'Y<hHP KBq^|&~S}kk7eqF۹jx+#LXZۃq;{Yo`a~MrLg_o?̏J|(#搠n^#}Wrd~7 ^3kR)/Ƕxoqf0h?ট, 2Q̖߫'@>F\ ]|2F=vkKSw\vX@<%!Gc,V&$g0aI˱N5LZqhkvpdlz 4l2Sj^9>N,=/7iJR KfP@Cܽfh ``8U^}#/ +ެ(9WF}*!VBR4цV*bpۿXk416pr EHhyl -;"V!=՟蝞<92vÃLxܚ=y` J (C:\z!tHb]q17UF1KeToB\8܌7W4d:qH=S^O?M:G̢^XxkxVⶶ $L+ n8WG2ZI!Au9'Ɠ?T㋔.%iT~H24#|7+Bڅ-ZZ~?4zk>rY&;:,Nd2ÿCWƱc?rTߦxY-D(Sԭ gIO1=H?#! FO~xԓD?Ks,$@V$NA\&$]1dA4`S3/H0Ǐސ??}?yh8 dkr`3|L`<[a5W ւ VߒUVN@#q@_VD;q8h%].&PJb>egV\< cQTre WyOwTҤ.A)nFbgݔgQ0}}[4FYO|cК|êൿ̧SC.>s4[>Ű x9DhӁ2s[FBcE`BUlSq_3,* pC,pp0gq֒oN K˜{$NG6] (€ۑ^HWk 77+|_հa#-[}(Ýn^0I&mJ4mK1DLlp7w;o.uS_|_>7"C>>Cyb|;2f9B%扔GW8]X'e5ȳx4 Q-({HGa9i8NPu8h"V!ʴ(8 -VUuY9tu{0mSM8LD5%$Z?q5VK&.0CI8gjҊ>3ƤdRm p+G!r #:Gp"Tb8g nw` #RGK}4Z`k%CeɄBC>W'{pZ_ y.zG،tCS8H%Gs0/q~}6@jG/HeG/THjGj- J~^ g&S4M@$d$_eSaL#c%.ўU{ժ6wdX k-쐞8{ų2 2&ɒUYJ ޛE"#|$<1M 5Gm$Tm+BEm.l_7}=mM/"ѴM]xmpcMSO axL!DЬHiUTl^{3aLZ}m6RjVvQ+ $X+$HX|\[3]p UjպWRnEޭt,}ۋ7s ڭzk>q< LUc>`[B\W2c N t̆@cN6+sjFErʼ1͟MU,MG/Co{?S;s4Eьvљo\adbti (@!m2 LgKXM2['wJ֖3V-My IEVփJ)ݥ9f$TGEE"O̕IBa柢yga֞i{:U%7 ^K/{ -cxACzYNzECYtYms`f &j~FHUOyF6R7k mj}=TqS-V$+ʥeJDŊvE6aab2 ߽w.ʲ"kN49}Kzvu& ޙQs ЪxU;)~]mކV% bXv qn<9~TÏpad)3BhZ+D7C .Y.X$"d1+"(. Q+מVfj FP76IZ:lV+ zƓj%"GiI;~D6(t!i&Sfܺ/ޢoezz*vP/Hm83WB&hGlZ >e+!Dr=Ny`D&T' lL gb9ku"&H]8y8̔jMic-:'?'2֫#XA;yEb rZfK 35nd`ES_B(GmHjPd˕5ȲqR^p&GJ]T*bV ],$]ؙυ0^_jZ"~A@ITK=IkEDeKn 860 1#9.ƝSArDŲ\SR@<8h2SxOPZڌ[ ;z;N?Xm?;~vЭ JMT)-e4<ޖNE<tcVX)TʤYը଱Wo^?M0b.W}TT:b&~(N.* ve=#D[j(߭PWV3gjݠ'eheehEeVވ>Bo ؕbC!+F;rE \6W|~KDEIW!:39)y7R[ieU{ZkHZOXOSVA.[AqZt*٨djր)z){\8OM8cٔ4X,p!xspݯx;qV\WE:FI$a{PxlaPx _]8Ot "!C٧FX+"Oh^ %p KTg pQyX%Scj*.Zm%'9llۉ|9TD[ڛ2(ȾK#˼(kDžL{:*Y֘\-k&!ƜLr*q?PWhCݙ:1řtiT{ ҨޥN|tpŒ{rWmâ8Xο>BC2rwD-uI_=htG/ Ys )체[]coJ|Ճi]ѵn܄B./>/ީmM {#R<CaJ##;h (gymO!* x̂Y;~cr8pe80MܓbԬ=ԚYUlg\꬀x$U"]\ s:IGr$Ia,ׂw49n BAH#XyLYcMiJ0{\'GF>e+"(AFs֔.C]kƯi΍BWV$xwtB+w]ŞXc%s8~q<:Zn]Z{E9(G?.jN\?FJ6e@5;4u{ !9J;њuΟ+ň3DV[zp&|1 s foT1gJD4԰#Nʍ(Wr8 4]U;=i% X7-H|z.e!YzRԐO4ȶpai(R0ʁ34!iY=~QxvaO.D a 9_-Cq9‘6phneEP;dvVn,V_nR~'%禘btҫ nu*DH%pHL#MRq?< W>%O§L{9Re*\Ud;9$! 9+A мItZ8K P(TV,8J7s~cIHx; aqΦvv5D&9 nŊ+>N\ǖ>ښ-D/yKJ~xèFʧL8.BO]>Dt0Q4`"T"*LDӉpeB)&DT'SV&qպ&,^UGĎ622ӼO$vTsi&$')uOE 8*$(]}HM gbN&$ ^Pמ)z%Kr#ܯo]xZ45| [wk ՜-@uڳe5E!\tzTXO`a5ng#x*t)x}HoAџd 1'V24Sf'5qA{N ZrjRp+Y6Y;V fPQώа*#B5vzF·>A)ai RNCy"2Z 9?(\gbx'ŢWn -vgq'xxv:|WHŸeP(\? :; F+)^0kU_0:pЧ}z%M|dYG?;c+0k f ߌ[jܫqCsEx<Fٸ؁!ƺT1̽-Kq-M]e cۧ}z H8gg% /n77)p5 z"W L\BԦn ZyF DVJ b?xOz@I;+r>'.I# F0ѵ6l%DQJ 9!yZY]afBdgaǀulf# ?evlj$,W~=(P p5<~"'$S^v,%Jn% * C'&&}+/][*5>W-]Y/^'0KӢF!ppk85覄/  PkTQN(K$` h_V0 1*T#}!VrHt[ۊRG`mȍ /ȩgf'S"=Ћ@G{z9WÔubNl.-ݨx!9ry%mx?YWy8ݭݨ C?TV4EoN?@q:E/BOcf@ꀶyV=5wzw龳߫4$UU}W2UZxn9\wꯠA!{䪏%6~1maa_5o;pJʎAجKV0'S}?lBEՊUҨǥ:ȮOɗlP\wanlxw'fTֶVM妏AVwkBv`̩7Dno82Q$X8d:c(Q?Í 'o[U%&ଖ6A+iRN&A%+@ԵGE0JgR+vV$ ,_o s2UJdea N成Owb=kM*ԣ2sKOgl\'/;Bv38a]6i` ̅X \,T!FfkQ&hDty(%^>C~"BТ@1Ho}d}l=ۃK'e LޕzwBe`4ؕujZѶ/8w}M!\)0E{ 1p17Q ͫAuJą!iS@(mWkRF%L6\{@LfT+jrQ8&Gtęc6?Ny OsJAX/П~`W N?~9פMi"lM姣u.ӯ~hi[4%q A2lhATq@XD;K0XEdNѪY9X0VxQ}@vF tCde3 xz]&X?f}#Y6xfIjZas.JR#oḍ:3rU M*|4v1wP9]od,|xw咕;>#3su~hp* _oʺIg`DGH#V93ϳg9L>C K^[v'tɯD4Սk)iWPpuKD"'w}odOb.!SWoiPǟwθ;yN߽Z;z5zmk !Ň V+Vsא,N_@ZTs2M:{OǬ h%a;m*WFZ M~}x9HpΙ6 7]Aա2mœSAFBvu>RKVo7 kµehD5_zQV6djRwrN@B$b,Ny . l#8/IS8'H Dzۣ6?Yk1nb֫ gt- Խʖ7ym̠Vj|w-WH,\Cvg>xy9z || Z Nz,O[pOZS̞=kjt@~[-Mzsx%~=unGkAЋ,Uۀh0n5(bLʭpt9|,]4<&0MPz%RxӾ. 4=:'JIx˩dwzcaq AkH?NpJwU؜JԷ' WIʳL׆nǪOœG>}k ƛ#W`b); )Ls"y(\hC:UG_wWYu9e6~ӟwt"pt҅$ B|]7R6iUy)Efx!EwUHVC§u4Bm@*›d3 @.}$R@$ %a$m%,H٩2A%S]T?wV8w OW'3ZDUˣ3 3<aS_|r [M.iJt՝Xq&]"ZWy]e~_p=pw,.T]=) H}yUzquh\`m BlydXB1 Bxq-T޳AHEPŷ/IH͝^ q|qhO,-YpxTӮ4i`NӮs̋jYxtAe|!p)w1=d-Zpr2WBڂ`Oqzq'ylfK0 AAkRŶUUkxIڳKd-K_~& uB~|8@mihADpuK5sp{N̔N3MRIܐҍwjdBzz7%R(E!½pP +eqJ0W F[jYeR$kcghC[?d!F ʄA]_SAK?)]JBGlP8vZ⟗kwa'߯zp̿ל ]xͤh apE Dh0D)2LengSV)G1y_SyhF'd ^RVi'q{JSkc|Pl{WE@Z Đ|@~pcfرZ=c4tQ6A WWt  T/&GI`Pї,9{j~f` @Wݽ_sr* y∦&/NbW5 &A5>z䘻>YlP7(XW9 UF X 9HoR:/Bev`,{z8Zr B?25{5cn >a na!Dvz_epig(eh[#:c$p%^`W)(wJ96{>{x4&%CTWߚ {R)T{~#M[y#QMN[_53<]72ǘ.olyϹ^".w+1 ;Wz 8uj[A:IQv!OZNϽV Ed=u[8TNTkT)vR*r?ǧV]pF;j/I#-.6媖? #pՏjon[扰1K-VT@mmUf%)&~2Gj7oR:X#E[O~\Z ~D h8COH8,Q )ê)K@1a|GSS?\wc£?3'ʶ!aFĚ2j$V+ײ0IKdi )ͼXХ.`9$ b4q]{+nekPAC]Ҫx=WaŐכJ䗀fs M!9Y Oz.ԣӥЛh*H-p9X& >ZwXZW5YUMlgnoяaVST^TdL PMȸߚά=Mv쳡 q̒WP*Nrتk AHCLΩ 83XH/a,L`(Z_6yŴ۶Ch ]B;*nD](fe{)3o<_GKHDTijkFo8~Vܮ[ֳx*_'go^.c*߽~l4`7waC`1in~)m=תӆuYT=Qet\BATL/7Skɱ^NdFx1/4 .{qcStfJ "&PKׄjCnp݁}j4/v\oL/"tWH/*ɶuEUu og8,"@ p (bkK}4]BW yB1*O~Ud{xr]a3</}jžT\Xo 꾆ԴJZn8ཉƱk SM 6J9ouWSbɪC3w|ug(׊XI›C[~p/|g (^KM}uHNGğak:)"8H~ܱa-r6(qd2WJ2륜nzd@^; `S],`ň"e;Siq|7>6slSw$,K=@Ҭsрr39 q\V~tEz|r(C({O0 *>(82k5S- &kyvRtE`^;eŔy$|XT=1cFf4eRb]s2nREz 죖aAZ8l@nUpe?Y 'd%* @e\C*vqgr0̢>u1P~ <]XXhׯNka\0Wǿ~2;=# DJxhkPggI%Y !,-iVupS!. @.(RlQLGALaČOtzzս` 46Ӄw_zbqnNn_e3i<])ºǵ >AoF<*BJS@ O*3+`/HWQEL36mWRH7pRo] nOĭGOQDE{ds.KtTA&\2@,g\>^4Ow2<\5,/7u3Ռ/afśHLq 5Xv&3⢲S |~ZELd *qo8WίU͍ (]/J_c˺orIah#'k3>iƓ~N(t"@0*?=y{.|K HhHM{RY{ɬV ,y7GEl:Ƽwhۑd@cæt*N8`1Mޙ0qSVL!fh6=j~P0hp@_FBy8CAiDq,a{ɳ{ˮ|R'+(xL#ECzu*rxLNwӍ7uң}PPRBQK>cc ?.8`K'(=.:/tҚs$8ͪ։ Ҥ E]t:dWa2(J! =4I&~~(`_P0}sPİ/L|e 7H~.;#U `LKxC$g\R l9o84fŪD)sΫ%Ŏv ) EqGcd~ΏH陝z:jxtWlʱYeZHuɰ N.e՛0wC!'/M+|aN ߴRh0Y&PpAAJ:yVZR`\^iyX..8LOc[&[#."qEMbwZJY\[)}?S—ą Gwb:GJX+\Fe3a7eY҈DNo@>"@V-QuR0:R7Uh®HɴZx h:`V"n_.0(SKɠh|;򊁨#2eVx I$ꔕg*Y'{Q ѶBXE+WrV9%? .,ULgɗp:,b Aڂ@e`\Zlv+0ty`һ,9E١Pz__. th-c1mYwVHŠKiI2ҨJp 6؈8h6k|ްs!bbߎ#L\y=roާv|wRi,ӣnB=`<ӾZ[8c^<^y$So~9^Rz 1 N81Q0sV'v:vAIo5tdԽ! нP+|Ю8AOU1HO,x1O܌A#WSٞ/zxY_{rVPO9Ntco6N-JpPUp:yzzjQ,9-Hsi*կc\<79|+w2L],m>^-vk~ L>E$ A NɎ ep!g',oy 95p885,|,MC?/-Z9zwK!3I١wUBO"dWJq}0S7_JJ\"k8l6B< ,9CW,J!Oy%htE^e~ {tFHb H8R™Sk!ַ.n`苻2aYzB+U.K6@f [06nVp !Na4kCd7r!HiCYOwOĉ q;bQYYgH!xx6& k\-3|soezV?+XlN@> z.J~[)6%PF'r [t1  v oFA?OCDo\ fx* gz|b:M T. :֤9ʳ5وxP ̒F 0zeUgrCvH*Q3Cر_nh' |*.J ([TB7`bFrDAGS"O\vP4/b,IZ.ms]2M4w|,o,6DVQ uFD2 joԟvQ ٧^w#{G N6DE%u7|ΖqQTFSUOd!@]dfϠtR@)q ec/GONV*N =n(cf"OV1E"p9z 3ēcNY-@]t1.U,ؚS^+ .齎˚/S ΗG;.fTOAE<.AXMGGvf:ךٔGjڀWڐ1'Zc78wA%,/ Mt6N^ҵ_\K}ZĚGS\"Pnu29e05W#fTzUQDڈ_FtFޓqpR_zS@Sұ%a^p5ghȖ kS1˛opc{PǗDFA2\Xa5bi񕇷Xx‹[8p/Uξ 68- oǐKsOš`ĆȿƗ.5W5-q'xG|Ñׂve'X|P&@1Lx?9I[j';lIW|ւ5Y-9jwǯ.#qlHpnJJ[չkԝ :ۃ&֫Pl=4m5Sƕ%RЩ{ 94"! !dDhpgYl? MJi)zN*.R"*<}6B!`@{qqȭA1Izb-J TlCt%&W-/Q]_Jdԙ9 &MygcܝRM<[`Z/j<+N)Piy#20]PZR=ӒZ:cH-˧qn4T=)n &JlP("<\ / 9n 1c5b)Iqe%nYN4Li&ȯmGu ;[k ,oNOlbz NWڄyz D̛|n]+]bYT9X֣R|ƄDz_/"5.6W?ZɔU;܏%ʚt߬5h}`Q \>vY@ "crθ6v}Rw+>j+Oݤf|dkչ9b9'-$V8TxZGqtO*χ˘N<up%Ԯ@ t_J^*T?*Ч tW>W/!!4>LƝl"ړ?*%'c)LFI p t>O;H6BLX5A{ Qn^5{5jȂ]t80`V&vXgS>Bzv:9rx"aTB͵";/hǔ[ޱŀCJޒt\ g,hb?oDY\Kacɇ`8*ǎ(YK\k! ¥o Y.VmkQf!0O188Ar oD SSB%oP(OCSvbڟic`{KZ*L$gO[b̑;yZp EE$#TIJSOEz.KD|]:W;'A%-nqr*UAl%:1<֪~>ǃv8ɇOYìwƃa̙ݭJ īdOL-~g9 ek^쀹ڞK3k8,LUAΧh?-Ҧ7lu+'eʣvGѣݣKQƉaYu\@$YRu㛫EDxQ5Vىsb/*e9K jl8_v/WI=g?>yy׀ͱzF fk(*Ahuo׉?]a(g)vKXwW}.(q%#ە״i"%C4 $J)ɕ+BGGJhp$NH$+yNA)Fm55)6SGW#oJf3piR:aV _Y6~;6{ t**&G]˵Ր~Hz_$,QfKFLjFn񙊔+<`(dKEEc)5~JBn }2KU-%fQ@Tb3Ni_;o&{h`b`@za6B 5'PH[_BQZzEnd>[@:ß ܬdB,Biu-|QN3z Tb=M$sVݻZx+-ι8HB)6=F6L7Iw[}Va$qÓ fk\Ю0^=/4d;L>9PL@cU PY.^2ybk:B<,yUۂX-_9k*{C a|;)bwa:6:j"D\țIG yAR[xa`T{1u^F!‚ޏ^RfJӜ7Z֭>PeR b@0[ `զUEnŪ 0OD}]Ǘɀԭ͂N4U(s ':ZV) m @QˮZvvs{Ȑ :̖*6ƿ?/1+ڽ2S#]&NftGӃK+TX GfXeab8j "ϫ|bNݗ44Bm0: _ɤ3no{;ZLHD:/yc60FM?AX4H:ׅ-PviҪNr%Ten'F?)R"̿Ear_{vŎv5X98L|:A&H{slU-F/:c?MZяx0u>-$LQOMwLjX| X 'BƎ6 =%ͨA#hm1lA/XENG}nt \}0!6 v.Q:MS/OYy`u8܄|(@F yqʌ'xq%:Pp0rZ- Von ⒕:@sPdK"B:IWި2%s8|;b~=Múq\5HNu}9-*1yr.p_hc'a.+؞uNyah`{rV_Bw WI *3jpb\$VЁ>]:NS(4Hyzg$WzO.L]zoH HpuzIq3(5HJL? E O|ԳWwGejYPctN`Dy&"f/S\&q%>m+_N(HuOdn9Tے%F*7[ s@T.ԢUW 㸐t1DOwvK 卞06R e&xI-PX-jV^'j׾"Mq`dI*TyW.ԫ `;:xVQaE*Gg;!J>NM㞟ؔ.hةS,?k\!#@v@L-B^H&/uPd8qxa I{\N@LًwB?v*de+nCJIP63l\^{s|P]~YN`Xn3V_ʆwiXɨ(G`)Erfn=zX)*3~*ATr@t~g9TڐEp3CVpT4%P' /(acT2 e۲ 8yڭP[[m\*f&ۤBs5} } ]㣙> /8i߃ fAOQ (.nΔĐrz qr1|I݋m8> sWI.4j7P5+Pj XxUUL h3ȼ:-Fh3s V #(nѬ@ͩ\oԞN<4do=xc"!^Pհ]N[nVI545e^[Jd8zFUDžꪮ ,0 3 4]{^g Yn!`ѵdppʣn"P:-]smOrP)YG+ZGvLsc7ͫp0G \cffyljN3<(C2σP8疛ܼ\nFL h5 n!IlytAZ$i+jd9ɢmbFuMe|W<5E@<+7#t^?qWܵ-..x0v8qyQ Wv81Pg?1|@\:rk.Vt ׃si[-h!?.m 7}=BݸڅwQJsks5\,ipi.'1/rJ cA;@KFGE~3|$gm^~8+*㪱p.:p3_Y%;D"bC,&|0v& 4ɪձVjaZF Յ=fg7Gs͇w%FӁoiBе1L4-S |.E?|ҍez:긇a}Jx4)~ |qܡNodMԚ Ovwf1 Bdp{G`܌R˴L?XGuZVkѼh4D|ƺ(EE/zU*&^xsb2RT!4]sMUEGC> ҫ7 >XRIxr)H48au-0DҍZ]M碡ɽi)7Pk8#'ne{.T- {]M:!20^:|I ݩY5*mz_.dtFS|'SgЃWFx[}IDLc!g'2)(-3cZȺB72sc8*͸ۿàCLWe-ˢ&\ftvs̅z:7&*4m֧vUZ/^cto Kn[Zz ZB?nS+*Q3Wrs3Zz_'>Wh+*J;0]wA>5gWU+j؎qMFz"dr;͹v+Ƴ*w[%vW%ͻ2wo*!ʪëśe?.ɫlO+''Y$Mo'Urz_ z̈́4ieXzM(oㄻ*'yO2q2ǝGe>ȼ^P&"奻"S&+hC*w^aƬPedp'$ t>(0nMf=<.6øLFm;{5t㺏A(7uXtE?gs\,?1iƸ~h&^UDK75>W>ȗ*rbp/'#K|~'.q$qy7ˬ߉'JiVIO2sO*')>迪/+VѮH6$Z#ގeW%!Vo9n~'9~Y_◕֡$d9X ٳYY>2EOO(m$K8b_K@ <ԕRǟ>K}lxD"}u |:2jNZS& %[F:}>|~RoZ>±^Wvv%8~睩NT3/ݯ`\Um keiZHZϲhwSZB6WuHwU9bS>_vСuq)̔V-dц2V*ੀeExh<͝)#f/a+PͲ?YC]H+ӖHgXޛSG:@ZHrDտ+i,Vzj 'G 琀gJOR#r O]AGip(>: D]0MUZ*'jjl @ W,91bd9_1Ydg⁻gOvV\%>1u,t![%sQ(D @1m7B@ *5\t\,`||xXד(Rw:95Mt&{+\b"Nr-p>o}_m9Zɼ17eMɍa /|ZիɬTjSrcUL- 8Ola |N) cAkҷ1{q]On;?J 7g: sFɍD {FɸNl,jP߆(5o5; n9;ݦb**>a5Nlz_V#ZCl7 W(x{$ﮐV:p4z73Y^X~(G臐rz}OhH2ip3 \:*ԯ:fx ^mgЊ;^:MKN;@MZӕf (SJ__j2 ZP+䦞t&>X^Qnj55JzFjrm^G歐08ju|kq3=}2Ƶ62e4yҿ{5E9KGi|aa/:ÿk͓D}qϭ<7+1ӿ:3'Zх[ u 4A!WQ|PkNoė*DE/+;:Y3kn>'OKWm[*M57HׇzuH׆?Fz'?|f0-g)E"ebAvq±Iݞ&l3gOpc-);.3gDLw!<-F!/6o /{t[~F%x7uJ֢lo蚇c]%6Lpy N7҃z$WWKDk4I'DZcЌq7p\w ėaxg.v23 O~!Ut Ο.g:+G/ #<,2hxXD8̟W| USDz'SW ף. IGv7ÏU(4НYQCY)%[T_RgI/q{Ѫ?A "aJUab\'_]ml}l{[$GA*GF+}1>}E-1ηpN;}9Z潍[=ii^yDbVPgYȄ'@e>ncG*C=}Q [u`+94̱4]' u'ud78{|Tqhy~*8q1x2wңuf[Vr(8s4\t9 a2Ff'KcH Ah9e On[v"rEueYGCJ&kv`p*"1%,>ihR/<6yNWϸN X]K ]rXo魜0s_m3 ]F lM-w)7Ԃ0>h4hU"+ 8"؁y0ո&HMf?{uV\7k,킌RasR¶!3Tˎ-> b&1^T,7_CySIؼqL=4nObˎ}'eDOI?Z5nw >$7n/:MRcpjB<+RȌ "%I;$ۋʑdT Gh%l2FGΣR /jC+%oELN#ee_"\WT |##YAhD #}ʊFv~2X2{k2$p%{PQ3"Cѳʾ[ΐ佻W~!{~$5H,yk'Nt_w4xw 0ƍdFOGQ2@_& [^ᠧi.ȉe~|_it@M롋x56~jh'%PD SLܩ0eC K:0ZKWY0eMwFd\Ze1 ЕNr%d_X_SӷIH Jf}JW c@)_+~ ](0P}Z~隟 d2Q}\ڃEB!^}LsR!K_Η_Jg%L傼UMsW;&^jSCӞz ^z~dwf ]Kj- /gɸ2<glt*Vqz, ~!͞ zN=uօ%#>v4BE[<u4lڎIgTD ?c.F]#[svdVW^Ja3 mȥ b [';7/ْ6Yl9Ջz:[)9 ۇr#\U{9k#6pMltg8Tvj ӇΪ0>u a5VN|o,(ݓģGyU٢9q3_ H֫so>߭w -x%yP/!9O7]̔[!1ȋNW{׋go)CVjEDMCA덠Tb:)"gڟ*kMvâ^ mg;S/hW?rܚ]A~hCTQp9y~@{P*ثx/S&'`Ϥ-E{ !"y_'۰QP ^T߆z4&@)h+|wC'6=؆Nw?W%;Qe;-nGtT8p\Y>Nç-'"L1,):.rM]&n6/v~63 =SF]@JWfXߚq}ȕOZe7NS2Wwf L[nh6/H$im%vf%)Dh'E9C"SN'OwxFRB^.#A# 9\v~1֗<3`Yf8-CAt"u%_kgNpAzeģ |uˏst Bԯbd 鍆^m/&ihdy<,v@*[A#iڰOZJVyLnk#ES>GlOz= 7r6 utX.$.>i}AƑwV̴AxIX}p/]@[q97^YʱuO=1.ZڙqsY4kKkM(AU2\7gQV"TJ_p-k,9l#5|۩=l-I$PF'JvT1\/rRzFoQ|N{8U/o;7 Yn~VQ*y.5Xx0mj0]6d2%:U+)pMifnLTӤv秣^lֶ Mdo-O={V;ũњRvv.Lch\e'$x ӰIyG c6&.wIoLkuhP.k{DJ6xL@QppTQwho { T5KWW F~Wc{Q^SMe'݀GuwgT  &e"{GrLA{eRko> u+m- FHI ODJxgC۲v݁Km"sr>~)]o)egtSOы۳.!gW[@µdGޗ+G^ȨuߥUSX)Z<$o2F.JژY@D|3ޜ-k#6]}Ζ*mNUd@EJ;,eDN79:D 5gEZx 隬˖ |$Hv]~(މ~KѸcӅ44*ro4fTns3SƟEiB働89zÇe6.bf8xڞLT1VGuLnMZ25 zh+oAz3eycTOzdA!2J ɇGk'ka XT/;&({ %;ɰRqV G+PEYJ}TKT)%UjzuyqԎiwhg:|@&IXW#o䭞˿i+.qNt.4yYn0tmz\0d{)0EAoOZ' ȰJ>=FT@Eo|h#aH'G Fl~؏~%k=}|SU0OwNCh+*#+]^=ă^{2jas>_Nǎky2,@gmd.Q]Oc$ixc%EUncJb|i[ެw=WoX ٮ%6429oB">HΒ{\=%؂i=izнPuȅ6ߛtqJqdzӦ~'}[Մ琧`\9 <4o{ @ՠFZ1tq.ZJ$3-mdR> (LB~ϣ},k&lǓA’oUcQ-δب>JcOiزUhNm%6̢Tbfg: i alǶwgWqOdtT vaҧ?Kq3otKUf7]!i>u)Ϸy)~?֤S&]zUBk:΀fĽCCJ3q6$ e9h5$>@ e ԐN73l<ٯ ^ ;~? , G e'rqu4wwzCꢷhr2s^ja udE37KTlzݪx)䳝A| F ҈*HԄ{]GjV^+Ev=t[ƣ00@i}m@OhVhxBT߫UJ7Tj>fq r:_lk/KA(\fLOB JKZbhMe^F7XƲ`y`H )J5a +dalYDmI<}x_Y&_ZpxPT%W% IO9z%x4(96+ կoh Z U8rU^/R,)αN t%:=AQCNUX3W_hߠ“ި-f$%;0=AA9찔%Żb+)Q|麨/g7\|\JHfiqA1|S p|:}OC 5P8]!`Q="guWS{}am>u j[1'0e0]aġuu˾Γ5qgK=~#S(%y #= ]I~|jrO N Ec+,2aWgW1O5STĊ*d+i_ޔ) Ƙ/ NPj ﴲBl\huŋ8 9V۸hzC(HƉKS4y8{f0EToZn*y8ո*>VT䍑-Sj8p,-AU\IXw5s)xvZJ)FeTuPE[L8zM ouPvGr:]%<_IﭞEpXr:tl4RxҖ6C)LSl*iyalӈApc` a4vҤ07yi[g3{\A.! M3)[=ʻ:# 5C1 k@WMʯf eK%{l,J'.UA$e(lN3Uj=ɮU休H ;ƹx߸t1Afdo$#sү72Fq߇Y"XQo(J9TGi^,Tʥ2^Gk4:ߍiKxVg|&:oލnsf=Jv?~GAI[u;S`o'!='*Ӫ䫓+ =O;?,>,GGGV?ᶙb$m5ՕE C7>2xqT;5Mެ`hEIeJAכAݙ)͔Zp7.?Jv[E7w'%n?+a{Kt:#*ȫdnr]tK/覻wWp t/07 ԓQo* cP4+S׿AGcߌ;a1dzp4}W MDAfUU6T-fe: 0l䶯y'uН`pb7.5"<=oAxPo(x5DxhhxPg,[)> W2[9EsNAL+heD,gnE&#!)hFU,,aR4+=3z09KLwb 4PK RXmEW^#OW Ϛy0tLza:E76GQ'ME%炄t\xRdڹzCEkӜ].O2"W}11$VԣY3YN秥0[5;itI{ecޥ7a4~= B6Ge{AȡLXV JS)>/+QIxeb߾4f0gv+2& TI48kWB3}4Qr T),+tzeb)4 E2Bp71,Y$\&yby氛;->";])AmUCV[4SdX[F|vv@0k%i2G"SYS;78@ 8whLί 3[\y8r"-eы`3Dn#wwHA'nY/$OGoY?7( o{ɸ˝M~ KMYл&ԧ(l>:soQ0V$kqEG)9! S+7__UXi2+Gx{jm7>i  _g 魠V j<|yS!$}4L5a rŔ!}/ m>"sn),VILa2H"۵jg_2U;S) gez}p&i ޡn1}7,x/vA` tk7'x.N5BӚT/ETZŠVUў]!N1jƓ_,APIӢ.e2U.dUmH)Q쾜7AMevz=:) X* |zYqЅAcNvᑹNHvS)ﭟӆx@k ^@h<A:a0c@*qPbF2Z=ۇRbQгVvUHl mކ~HTҤWi鏢=!t~IgutKը&٦VOuwU,J,U ϖM'Ո^Vk6}94UD|a zzK#%|w}}<foT& 1jv]+m%ճ]YrS>E$*ذT)AGvC if)필ɴ1{5E"cg&QO*D$8mA|y. CC5^+vj"~Ԓow{,\f9V]*4],Εs$~Rp 6~0ڃN?34b᪔f^;y^;!'j˰'T'ʧ4|ņdw$M^>xЫ.|?mXK>_-S8[P 5:2)[-S11 ^`d8=ש//[Bt~,!Yd'PuR}zߥ(>*91P" q 4Qb|ʎޞ)C?)q'l`gu PCt>orH-g6 =q(]TU^Io>ޝP+#xAg'exeԃt p6bgB@oLtɷ \Ck3ރԄ;[Jx*QV215r&ɠ~c)P{AGVXO; i0G @S3nYoJ|>4}}j|Vh7?+Zi 6w3!E$Bg?h\X'27v5O2L؝0 w9R +}c "afGiR%Y~V+KCn.WQzJeA]:폯Fo7? 4qj9hzM)0vZ6A+@*j]tU(Z[Vuh3W T3y;o6's#0ryU9~D Z^LEr`0;˫+; Ъ> r~o>VPd=l#~qD2QQ g5Jcb S7г3JPwG}JL2GbJ|]S) ҵuTqD!*hb|iصHЎ4rx`M4НD2*5` v_pAս¶1r+C1J%0Űsr`H79Jc'u؞>Ӟ__giA|@Jg?N>3Ns(Ǡ|oW#qzWSn_?}[`嵪[ĝqn[TSc[w"撆17;!d]!*Ăߍ$GT^QXs_rrP{+QX(hԹUw!J%DyNE9+jc#4׏̭9ˀi~y _T>qdg1z2r`EK#OK .Aq=y'~~+<yԒ2ԽSFHo{&%[_fn GtO5tZYVNg4%IC* :dB%1kXBxaJ-&N9`a}}NU QXx<Hd k.jIXc6#JfqywTV/ԠJ)<UE.U(h́.oZ~/BZt>Tawy-lKX[5 8oނ,8PEkWo84BaA =z:.aaf3CքM^9FcEO bs;OQeQHؒ!A\-0@Nt 'ݪn+# Br6^]2V[Y]'f7S C+Dh)8.p|18dLNU=%'q-˼ǽNu.eթq:a oMof.DD他:҃2;25 jɨ] W;O?J,h)g"qO,aȅ:HWP ߳3 ? A8ȤQAײ"`q08d"9Ö8'ρHkSzյ S9+g@2FA&mWz(fye\Zx]oXEeE0B!GZ_)Rr!41%SxC9N,HQ:m"eB/Jg7< ~>q=\*P\]jeWs CNI7G>8q+G j.?p2j\sz='k5IB@#)qJj'?m3zCYTzhFYeb5.hF<:FF[r:0: \v3YW dW=k'g 1C<u 2y1) I,괢$,=1Ih 7-dm3}6,#Nw@͸ t,kQF#uo-1IF4ЅMΰL-Ԙ=*DBpXO4Ƶ(Hch />VŊ){ $^]UAEܷgR8wS"8+2}]*;N[MRpcvFc[^Wskn-+NQ78x޳Kg)}|ܠQrf5(8仏X:NX8dG}Cuh4Q߇NCkrqo V /Pev5!yw4}8c70z%@OKBni nVyѭjwW+ӉZ0: I땉Uy?YSVJxg6!5gUTʎ{(GPPR{0Z'vs![j8~5svkjCfIܬna8)ҩa銔g2b1xڻBQNQaU^5?Tsp}pP-YCAaaWt/<21Oݦ oV_ AX? A n{.rQ]95y!ycEw*@emAoq<&̱q9 !rl,bJY\!~KAa4C.%j?l4|]0NoQpk"RdO6B#PUYG7[{oҭS~I&hНQ\0S;RG{(w/WJwDgcwe$Y'I=OIww+\NduΑ,U|W<8YwL nHr7$+TXB!OX}E"aQ>HhEv+.bc8r= v@SaZ8]Kb;? 8.% @ Mvi SyaBϊԺ_]woj ԀyfʏI'T#]A`kx$W>Yj"ႈ'3C=vvkdCk(@V9ʳ#NMP5x:]hORJ5uthuCMA%*fʂGqI"t4SF*J ^g:|7C=ʞ:WqC &\oB_XDN+ldvx+;܀qHREhV= Qa^PyTo4Y#w̫r,=&um 6jFn |`OPmѪN20IN;; =(su ˑ%\O:0ŏ ZCAR&K鬃û|!@Z{-nZC@6wʊ =Qqo|4ȧ?t!i}`f:Qe'xCoߩ/9lhU:ĸ Kg!ҀzW|_z[$D/q1d>9-8+:JD=;_:TDQa#iRZ9FV}0R~v]ֆpWxJgnӮCCl4d&]6G2ţ񴞠S9Lqۂ3stQvOÝ]X)%; v~76ҵV]?n}$5 {,K5I-և0]?!Lђ>^Nc;~] ;BK&8^5YHJW"jlʇB`n)0XV9a fg?!1R? & LY2Nѱ¿rI=W'g ԫ֋c!8d?N~!W6c}Bj_؞W(iM&]cV>^lM4Uku?iޏl,*Jc]wIa=\p*\:DC<"鬦BW<66'oK"7Vf BhJǾtf)BJ6Q8טs ˿iH_.&KcE8_MEe^8>"QbuE{+-6I"hD*_ umzǥ9N- /@o^#,pqaS1'MoV@WjyZ]ˢ8 .UĚj{jOG! 2o F 9m[ӇR$6IJd VyobNd:zU/Oo8C?<{yI-i7D"+TR"lq$,ՐDMM2D˻u, ȱL.!SYm,HGF5`Q2Ka ò#p)0NbP,%p|Ȟ#RNqZ:$o#VﴅzN-z䑥ľijGXҠr8HjXrhPPF 7yW^S-KIdP/xࣩPEGu6[O|+T Mx5f+ɀv[[qQ8$\ՍI t)x(֋xY9Γ-9MJ2՜έ%͹|. p1fx_( U͋0Uw[(|:OyORyY' SiHP(Y {5 K JCA}>Ngi菼F|s B+:‰Oƌ49T@)gDCRȟk7QN Us&>I&~%-V'H$Y[To\t4935~ C!]Ú,LaA>0R;zɷ|t.=]dK!;`ei9, ]Y"ircX Z ԅ$ BN_h>DrPqHqOa}՜@*Г˅{ pf/= I8iIފ<pXؾYџ\=+ K] &OԋO9 !Uy4,2Qb.lOɒD_I$`ua(!SA=-?g$]A u_Oɯ}#_zڔ_߫9t Aߒ.^Z ;{G˘L]o ^ca415f1;İR ÅeO)1P䞾i"BL ngwX+aF-ˁ.k΋Zz &:a畭O2xFku|qkQ"KԚ)map2d9Wgk#YB^6lt_dʷ9cKvm |Z/D o! v,7w΁V֤ P8 H&dyp ǫ W*Uh2͎v?+fӶ@En|t$x)/GmP>pS՟F6**d؊ <ʟ0Bvn!'Ȋެg"| &@4`P!@΂}2"q8J[T'C%V)Xq zJ7l->6 }FTCK剓P+=mCBP},1F 3\pCY x‚S$ 5Ƹ!Ct^Q@P>)֜2DMPu2Ikĉ!qi3Z~7|0rspe`EjzzyUY1+d[BzSNNg;>Q։1.d)ha;FraS J MY4,O_4+#a&&~`nenL{V5o$*b!/5k倰pQV[v5g;sQ3өOx#A˝1&fc kȉ$1#*k;;KKh@v,P8Mda;iaV:HE1r؞e>4:[AdO [.eKGP4 PRp~J 6J&PI{ܢ.vƁC4ZeдPd".t!59E6)޽ƯiEWIR~t~u2̗;?3lr~.`]t[.\I5巷:ʙ |Jxu7nv;3;X!$ t=`t[ҝ&stz uP:JƏP3 ]aJQGwE6XMN )h[,X#ev0Чtv|Ǒ)FQ`' Ude.^MGeWE !QOr2[ **ؐr>=ͺk4z+vA ˣlB-1'V 3^oCྫxLȽiB U=&=,:P2uZ׼+ԑݼ#*3cH0ͅvk< 6 IAx20Mщlr'}H>6a!jڱך7%5j NaM4AYO]Ύ\PKTrԯnH8Z8"o;1wwmw:mYͲu5g~Z0h,UMoSjBt R%j7‘ڛGBty_=lWV;DY]+K78ʤ Z9:OΕg=Gx1w!RTgB*[|`:]"ɣ|W݇C}OEɿ輲҅Gn9bڭ,prODUOl@ ^K5e`U R%葆<ѣѷaj^5/ODM(\{N:? 'NLrᏧ(}QtP |k{Pa]惱p*^'!Q*7`c`/ux"m~eYɉ HrK94_hϫ([ 3,|ԟ ?^ Ptފ CFo79 F=0e)&fʐ̤@jwҲ&JEh*S4Oo rg]  9h6ft'Loz8qĩVNe vZʠYi+!s<{.}/F 93C| C̫mb?^iD*]?qS ĤJ(gVtc8mY:'| #Ji,5C f6 2.iL= $dJzuZY%LLPApLBy",7_ԔΎӹBPBP'֚*cz ~ J6QQyi\v.O2֐HD]6_,i'n3bXQeg?Ht  J=٤;,%I ]JϻZ/j_vL Uzz=><" b/;ٓ[Ζ2/tn( !lRH걋dwj"QHKvtJAw r@a\}7=ش /^_s-~s> #KF+JW&q%hYӏ|-W[l`'Q;vQF1 G* ȑq!@!]hBU8yx>鬇V<6 y9/Mq" OhG"TEpB+rIupyW67.\U9LdO'4+.l^[ #{ـ8ZIdiUZ A{Q%XǸ}K l }3@ϼSYHBl gR7.{hd2aŭd;kਓXAfQ}nx$MFW&C5ɖ&F{<6σ2JuP,u4'n5dó^[61l8xsAjNoKN4ڡz9l 99aY;L@?z⠐j-ޕtnQJ L^TY K԰(FQS %&EvG/Z!eģPv%tef'˒֊\ey¦?p z (ӇѼߑeRz6d37}AQhX0@ՋaHVoxDi4Г"9*[\D鬈ꇫh:5,zd9?nMZiwГH8٨ eS q..cȾPsLsS09A9@-=z5YjJNP](g wyqS%N}`r>mlvK><^|P7ի>b\(¯TCfgj<ڔS]/+'̼Z^*͇RK9h:qRBLgxJOi30e6y-}S9‘9;Wks~"%͠W 0誵zJwւ#9n|y3ȫ؍,fWhë-]8lL̇O 19[rv(9C#&6@jk cBJ#+_kGNRЧ{]Sj0*XC]I+ox뫏xzslU~CZs-Byl]6O&i:z&~*j@gJCʨwÛMJ*H|MvO䇢S\2Ks/aK |m7N/+/U`S.j:蒷.pP@Jpz4%\i{wfP&- <]b5lzxxrlOS?HNPko헷Q'cΖ$Pds2-$={2-$Ѿ껓JnWT!a9Us1{*˓I^ ߉2-0yJA^= ŋ;T/ jz&,hbmo6]8=~2;.]"ozW gy̕!|19d^sNgk̥l=bk,O~^Gvnݺi/5%8##)A8 ݕch@u06` P n?; L4.3<&&UU'J^bO>'3(aJ:υI=a$cD0gΟ8Ww$ 8-˴$&}f1 `(14ڵiLo+7arl]KG+Il!U$r_Idwd>4k5 m=MK昽-dt)&( fDpϒ1:)254&qc!˙laT غYam*VgAwQܦMS)?Y%#UdP-\w V)W]_U)>}wW [ ;Cb2oO g*irq{>!KƝCw! Fo`5o4uRf~7!x˗n:`jaGΡEOʏ\M OdX F\WU3B_b=M$0 .ܾ~o8Q7)wh">}_5h:N_޴mN;|82;oV[5j KBpВ'=C ɪbwalJ7mt)©q@܇VqTwמEr=˯zvki:7cMImMyW)4/U?:)BjbK­6[\L#p6銼M~Y7l-`A(0$՛‰e l9A>֋ZVq sy\f"@Ṭf Fo/85@I]l6P۝D4k nͅ!t}Pl/&z˖ԍ\HAie>0 PmJݕ>|x;;1DhwByE8|Fl`)6 }AcaqaEq^4U^H>9G8UE%; ZTdRx4 NZVfǟH,4K 2]xQǐ Z4@:?}+vç]"_9U:5o57x5LݾS,Tpf6ĮM"~C V| \Q@"6$N맃 gco׬=G+]FBdjJG+u'̣{̳ IYXW'IϷfBf'jIk6eZ7E V'Vԝ0®5@k(kiǵ%a_LnJE_@]#0eXۉs:_tiEW9xU?)\}UE>^B.;:vKI O.g<qu ?]~ß/2FkODZm.zOᰈY)Ɛp)rNp|PMN2.mewW[R;N&VSfMAp򉙂DR |M5PJX"͠ [K MLt/P,2)t6ùXG. Zqhtjq9v}sZ^, ziv4O!Sp%ǀ.Z#ݜ5yՊ7VœӨIyɏ40hUq^_kit&EY96Eo4IdΦJq>&? N!+\_֞O_miZ _ȑd:5{Lu>m [`6 Of3xlM4q TrJB>Uw#Fovǁ]/M]2E NtiH7|hz};\o)>SbC) ;d$<1Yf#(Ѩא!Ko4IPBщtǔn(w &&50؃WMPJӛ*]"iꦔ_WxFg#I 5`:M;G2MD,Ut7_9C<^3^!}^JxPNc-$aYS-\@iJ ;g*%!u aOCm/_bA{+[w_$Ībjj_U&xr\3 V@BD8Y/]K`m@sDAЮ1P%dD :~UqueV\akHH}XYV=;oH>KT4P W(lr :^jPe 8Nq:^'ޗrtPl;Mb)ߤ SrU$Op^/= @?ryTl(IJr<*M3_KXйe2ay>4_.d]|%&$έ6]P z&x[Q݇tQc) PYC(2EFN4yn:6]+^zVVtfh[Ɣ:&d\!'rKiheeeSnsb9]] /C34 z\zD"oچ{y-#kQt)wV}J;Onb竌:0P4&u)x<_N1򝒺Ɛ+2JH%iEUOAaO7muq;=%%xZ"%>\ 0 9]P֎ܣj"t},yKpo"VEiȲBufW/>tM {N{z[>?9I׼DC`+Q4 V^buݿꫡ9) L$idNX|Ew;=ΤڿXrݶ.ԊW $/W#}4C*lWB%ZY iPVZ \p/UMng-uJl&-eTP*Wߡ3o{i/6O-E$A 'S>VX`"_`@*7p-Q To6]63cӘH޸bb;#U|wZeIb|l* \?[QX86bVxU<{94L*bEpt1ƠmR)g(wP?=N7/T+^VkΙc&(pS$%Vܽn|g򷯚JxLZݩ RA\nȟT`{x:oS5AA2e1k8>BgVQ ac7s݀ʏdMZRJwLKo?)ΞF%3sjN@V-ZpPAOVfǨ<ԃL~5Qij^y(hJ*zP,l/ANFRhYT+> #R͡TD|&%砖4l*RcHwuZIEug=T"_BAW_y6$,@E˥hۘ4u~(4>\ַX2*J4/ u7bU[ɲL_uROLm_{*: +)r*+A!ӗRN [}0vj44(- Q:'ѕFW5JoV _WM*'@L%ߎ]IE[JEKJ4Ұݔ,Vnm\s`YPʪ7P8VBSמ{w5*xn8jW$Q99M4KeԏZ*jpWSwl2zqg+1'GĊGB9)i(W ߓCN4-PX$5bAN+^ 8 es1QU/a|~y-t@c)߀(䦥TSU az%t6w^UQuŚ9dKlެBqpk8vXG)c|v}VR!D5D:GFeQpNWViVOo^tt'@V=#YrU;3U˝e9?9N*p$EJ xqPN'ŏ;TrXFt4i`5MK}F-:3$F~B|RM/r>s]$R8uzU|,y[]1Η-TkX^|fy(_L^hn+$?מtUmOo/{'hU =>G_]U&ĒLBI^ђ4JB&ԺLB^&wA%gxӀ2Q;~5 h~NzrtiW>(no:% r^6 _mXoD ?Y3,VJT Ic+T i{ȺCY0<2,`¦̑zPT Ԡwܑ6T&;sSgUX/dsŃTOTe`qwο‰cSkU,WZ-Y&,6(RL#/f#L&ޘSv3mعh՘_I e?hN|K;X#'`S!{4@RL!8֑b2AĔ4@fCN= ٬4EDX>C MQ}a>UX7HJk#FhzΊ8$;.…"Zhd@D!i?ɪ휼nI)R1EZGɠW(Ao_O!w{%M8tL+BCـmB0 .D qnly<:;*p$P*'9}`xXTP罶>m(d%U*"k38#%/ʪ z5$TG" PV} +a"OAt S#Z̨/Uep(/ߤpNQ %`cr:Us&XF%nU6\{,x;adg&tTgvZi} V4LLOl!Ʀ "ͫK`(tR(ܴ_x0j}ԩݼ$eԟh琳y}ش;ɮ7Gӈ[N2mOd2<-s|drw['' L8@$xdi9\ք$ ρ848``m0E8ͤeo" ҍ`WjLm C*j~_}^gwgD݊U3p t*+9ux}YgIwz0|ߟwP[Ic]g5Zcg7 lG/Vw|=@.ц i׎ h7ȏlec8ˮ̒gvx4kq(ą ĝ:Ίy{.4nQg^JIpPU{|pcuS?DHqojFke}G^~b 2TȻe@0D:61P1j{mS*@ML!6>&Y"\DΏy$uا-e4^enHXū PEJQHq"\9;sJbf47K0ްܸJt"C\P 'Ɂax0J5C$ 2a%!MՁ:\ӄ I 8wSրq5ƀ:8j [9,xSGH} 18|_fhͿ>' @7lwk"0I;l10&3%b ehҽ1}SfQԥRf~ײVo( ɽEelUܟc;cj ,-{۞OdAњ A6hRz^~dlzN0)ޢ|[&?VI>AZ9.Ja ۂc9Zp8bOZ g{cF'E aǗO=o{:QVd[Hީɸ-Kw-pN"A][n^6 /VhP&ԕ,tjGaf ~JX9mhoha+U dXr|D<98/b;E /Yt(UܻW!EΖ`l~Jv'F䗥<XR[^6v g^p-~"kv%uB-OHAVMKBcT30K ? 9I"Y/NMApɛNiPi)SP=p#JI@RY=%܄Wtl"tY.UU0#8Q3-S,ߺv[iKP xu~iz *yHdd[MM[l*pz'W wS隱iܚFOsaOS繦dj^Rk8D&1H0l"@{pT0u\W7PDeD:aJP2Fw 3#‚\bta8-f% =&`w,YLChUwR}FL(,آ)ؠY4=ZT9ܕ'? gmz'*BEA/7scŵFL2!#g/mL}OYVǛT^zPA{H5 ƽd ,#=p XEcNjN6x$U2*%[#(x ٞF%(o;Tװ\fjrUZ`!6q5J@%9eU=f ۓ7,/TEFԦDf˙oL+ڔt&Jԭ)WYW7YUs=2J47E]og*.)|b^t29)1["wԫg󳖱4'>4Cu:??^}m2_v崭=re~ޖ/OZV??8M꯽2ki&̟4L?O*/ [lZN)MrRӌNrzmՓIVN-ͪ4Nr+2Qʔ(P&Q))fUJm0`+#vem>=\r"]W)0eTAq# d[?+ C{,)"P55}05 #rWƞdyR&%:0MnUp,4t/bE\a?rژϪo$>$>%>}w둲#  zcPZۆ(Km铼9VӺ"Um)Yp~ܜKq|ATH^o* N*$94g{zM)< zlF5@^d}WK^_ NpRcA; 4H|~rXY̬iNWS&.PGJ__XU -GRP^ hJo-Ɗqkv¡~}S B$/,npq=& Bȇ%7-ʏ52nC*)EDAeI6PĉvWytim}slt?0|&k8o.,H/y@>,ԥ~0T14L1+3ފI.]̍` Gb20tL8$4,EQS5!/2 )ȦxU"Lo' a'Xw4E8In =5&nJxڝ3dd3˱SGiBp` 4 $j.=?g:ݻ_dxetՅtרSHuI(zFEŇpY}xR}-&N7/擳yhy94 ӈr==ߞK~>r7?g«E2%^&I+}jwDYX\o; /sODR sWD}UrӮdD[]VEmesj65RҪnQ+ܪxk%c(qBCWZX{ڔ"VWNk*fz[k[Pma [SElA6Vza_UnNӨ1nLQ9`m GmdU$i6gy0H٤בR'>zu[ 1{9y3f|wly߅86MD/Bm5¬uyݫVZ;KvF X'QD+IYw1sxֳT܅*4d;YNMUHxyOh#Z:֖BDtd0u" #qoB>jM=}ٴbkRV[nN{FKVO&  'SU_#r,BQd:X%::ً*Zd_u9Bېv)m%Äb<!X5pH3E] i]RM1S:jdWLL̊]]D"U1 3/Oe ԗ?,UZvHd6" <"j|\VudryuiVLЀE5-zj]؜*#sqJ]\ }ViyvOV4JB-O+ )z\SRzfM=\;ھF?ǵnaO і [n"\x)пw31a5hɊJ**$"C"Y.=N_[vPBp~ZQޘ9 ^ƒmb1*;Y^(\3ś{kZr=@(%>>%V2) )]XWvw~G6*jU$M5cG;tu~i,EeJG>I< з/ֆ<.2VSڑ hj~[텕(wXOTd[&| >,;fE4<,N?dLbe2 "jlq!m:KSRѣϾFץ4p[ox]&Zr*0?`VeB6)ʇ!Yh=l%+ii"g[vGyy-G #(Z(1”HPjAF Jަ!jɋ͛ӂ.JJԗ_E!]+YXN"!yFN֚Uz嫬^=h!9>7[.0s4u.@/Rl8 ݩ ^t|҂8(O:Da,c!ԹɆ8#l)jr ۀο{GU{$R[o\bb ܻcvOw.DLYJ,dSZrA{2rF Vpft/  pw]Ю ӣBB%]F*٢$2]Q!v ` s'գVY xZEQ/2ͱ76<.'r.N C#:m:{4bȺf䃠04SfL)tW21ee mBh7컆ܿb<Xpw %4&n\k Ws`m?_|Ắ 1ðp_{Hݘ45W#f{/aPo๚hx7kݬp< T#_5UVO3?#F<ΧMSrJ ֤mX$A':vVy4WCcq]DQ_deO|Ls\_5 >M5u/2[8KT؆W3v *p<ەLTXS',tCU+R6g)wͩyr 3< r;6ukUn|)u]yB~qy坘(KGVQT~+y} .8Ū&jbqw {jJAQBD=H@Q$g(v~xԆW3],I)OD!1\8ѐ3 {Qn0AY[xk3$E *;'fmX3hPgDb1DM63xuHFg#;z nUdsz~#;fyW x^U_@Y{5WXzp.BCE'Ì+&NSS c(TJ jaҩ+r+yⳟ}+4>Oˣ7xtrC"HODMš=_X_T_SQ JO.{ML_N$3<#US? ..+$&kjc=LKjcNYi鰓vKqݾFf6_kk,OEȰjrxVu"@ N߬)7) ,?*14y{5Ѐ:UW5$Jz+:dY}ib #%NF$7Cy$W1LeXֻR獸?qc"?Z٤ PI5ԄA;iɽP{íel}?./䴫dxX[>4DXx?Gg-ؽHAi{y=4 ]q$s8u*xC|`ÏVKm{ 9f}wֿ8]C}3um 8V,mVQq=D;Wܜ}RX3zYzI "66 ǚ{Au/rTK3WR! 4kȿo&\hJ.lõx(XD9 X7G>.;nrl?ɡ;O@Or]OUGlso)ucZ hbq)'PO˖Հ%k_lu.r]ˊ UTAJJrZPX7T6+RƩj .xn1?l~251sgEJ;/[&pp zaӚr`iOmEToLM Oi4̣B瀤Y9v$ }t0iDT]SBτQܬ_S%؅ێ vq,cCv$nzǁyL ?*)SE]35Y*'cPk]9ǾDUxRa13jD=ٱa&l-8i}c!;)Nb?yd[$U%@փ 3hIit4VC?iJ0̫IhFٞ$;ܽ_=%uih,1~YS8T@F.1-*I#I1bfP;,lC쉙0"$')se5t.RZ?JtLrr6|3jͦp yt qB~pcisBB{lI7Yay,E N|4!SEnf0/ĶCx3ZZon&r <]Y?gs}/{~D<#զlZ>NRahzpƷ"/sCwz{uIk^U/E²Q.7v ={zjF%$L[~YM~qh8P,zvk \dy\ƆG`& ']ՑdhcOK7Qe P"-oDfö0/ o%\$#h*p~M20eg2{J&ՊpDhOmI"BTE^Du>C- [8ᆿacymoy>|KC/⃆{-hh\E"}{ ys+u GO,UuYKNz)tyxXg 9];NR>ПsIZꐫfftewNcuñ?g= F2ĝڀ}HC]Ka>ӹ{l>[\㣋;9i#{\*ep/{)x䤙Gy7} ؕ)C*8[;;_Z٘'}6vǼ ) >k^ýAm*" ?/c*cإ-::DhFO Dpqݻ5Sj)J6הWIv~wWK{?Rs4,Әu<Rfl$- Q/s%a؝gZKRzW zoЩ7$LWL `Lob՚bG4OPQ@+`!n1+aYʱOo~Q|kE\{T*:j,piZ9{o}oL;ן=v'={g.U'w~*U3ۧf]lm7C~]B eQ>r 6A&S M14ĵY:f>vO:SL8 "ORy>&[P2ZWU<]S֜IWб9椇f7׾a4_R0)A>4]v{ueWMoueiIw,S`I}Mz WR!ZԿs涛Uى,r5M,S: >orn5vM;S2 ĀuީiCLv-Zkf?$ڃkB HFmd4i~u:KMi[` Æ54xmsw۫{2V=Dpم,(u gK~Aͼ9i?{ 2Um("h IgRkنOX ͜TaO2}"Ja'{L՗V1ԛ_s<{<4hjjj1!lKY4!UqLrvI]}TJRZr^?fu6ZexQ}+H[lkzimyo+̓LwGwinTμnqN:b)7uД@mNNI shyr/tA/·]P 8C "7\t!O1H`*a6Ȭ-]]-o w_hv_ O;}I+JI_X HLJ1P@^L:tM H%r0EŅOB_?YymE|_ S= p:t)5c`~Y~!)_Ke:OUfCG*g`Fi&gIplF{x9d0Dl! 43'}bl\w>Xѓ4v6skwfȤqSx JȒC7*Dᔱ9Ad?'J(B!Os$VwrH Z|YX-.bgWc;zJ(.\!tP tBT≰N|IIhCkHu7;mj,š2%7u"S&T˗j&5JaG<^ZQq/O"RV>GW(1_lS |ɊA?!!DV,O%jM24cNGYgIHm>dޑp]{RҮ=9_}%=)}&2WNS{^yۛ']0kv3&{ M}D׋I4H4)>$C~|&n {g/>uޯ(ܫa OgX,i{Mԝ5 >n&mEd=,vRRU`@{ε~)f~ujSx6%Nw^x)kcC!Nc`.0zUDt`mVrRaȘ?/Op+Bc{dr"l,ްC_|Ƈxfj$l$-F%M& I,e;ha{Iue ?F;)-k՘92jdՑ>uSGqƠ]}4pȫhFy: O8CD#pFb'mwZ*e0)Fs:҈^g9kI6d]Ļ+m8+K2IU'vVGt\^H[re 1'8y @nRyp`o:i~n%@B֡J)kj%6W[B}S DvXw.O߱p`Aq i<XV:` ƞN p+=J!^κ\SE6KȰu4 _4W,\xMj@+RH}ZCjy[CiR^Kx7۸럇6f0j*?`q7|yؖ25pxjND%jZZoWc78%S@jFXk+*Y)rB荵fg|?isu >cUu/K1eI&fuDEYpNM0u:nHAzdްQ?di=/pPuPR]@{[l;>^_}\?ߖs22a%`WSy@t1* )E¸kRr AM{^l<֠:4a WMD;fR[>a- nP E& iS̷;dZRpYl+hO11yNFV. <@u Q]T v"ўfTC 2bo.ql #AoO1o-,o(Qkgt :1ú}h__;[_9W_?S7ҍgC\_P} )C"VPcdHObOÁ6GӯV.x;йCUMmzY '>caEj[L)e?n8j,'_٬]gX\uW;/fqBʂ䧲縂"WiJuLȰjR^ r5Rx!}A(=/ΈS0yO9:c4.RMӗtf ٛx%I`h'cTѓuɚDAƪs\&Iȳ GM$``'J^URZwН!on*qҽ5-9&"^H~npQeN1k#KюTt*i'WSLJ+aRO6e,-y{ O]8V8]zjz\ tX{qYrqjSk謇zCgCC&S"Yv\2)/z,WWzޢ;5fPd!I{ِ.Q[jS$z!^D6XO_SkjiuOc2m@L D`435+s|%WG$WWB!?, ਺^z.q؜jLQpuytcP A#OOy\^R@9j{5}>!)Oc, @zSɵ >z)rFt9B@MBNr7 ˇioɟpA窐W+G@NwЂAS4fBޅ _.] GG=+f1r88P&"A<(y)7BmI4+! `cV/-Ey%U_EwڎwJV&Hzl,w1kl-za%qzp/bL(aJ$+7biQ)vM5uhaZª,Ŀ} 65n JB17žM[iu\Xϧ.BU cJtZD|׺.4E 85Iɣʈ\s6Z,nLiY} V I_E!5'hoO!ܼ͇ˤt,,ɸ 9@zA磄*,z*tua$'zzIBbPM'$BIubj|tSQ꭫Q[jts>!s\Hs!B`6(-ȬẖSphޥz=3?T7MXN<0'P CLe8`VX"iR^ȣ8#Լ!ŽLBNzN`kvΫ`2)NEPʣɄòQO40iFL9sVU1.dq/CgF>@nqN%KU4Aԓ$ kADl'y{ķu)5tR ]5p`@'1tq< 0;_Eo/3ܟ1{_"kAx+nGPU$ko*SI3뤯7uuҗ?ꔫ%3?kղkޥNV:퇖Sjڗ}Nn*̋+R|coڃ9P0́>{w/>l ˜M臆"bCh!#6nĔL-$@O%'$,uwMʨ+F%c]<1u}լ/u0pdas[mLS%;d0A>-T-gG_n86ӥbǣ^u(A4$*ӔJW+0R<P+Bmh(Z^"E(VOO)( aWI-5}y-аYTx[QTW<)XF#QjSbRX2MWذj\Qmߦm\'VVB [C љkƕڥA@LnG0J;O|?GRC;;ᗓ):uF.P9/Q~L]9_A!*sF];u '! &lZ:?`RoIVjmeg? 5y9~ָ\(ש߱G1t̕:; j_̡2G28&GwJN:c J\@:7i.Y<mFV^x=q.!/_ MJUO S]?n - !GM 9Q%,Oy@DQPlQs\:T'iPj]Z&onUUS1uӅxiM&߆QUv[n?sǼX@@QB u^ԷdS*_(ݺzvv,;pZ@ 60>fF۵Xp 3«@fBXS~W~,Q^@9g4M3)? n|9.]v A ?y δ2\dyKOGVO„aMʜwVV&WDot>:L%n^NYr50/.o\#h0M p0Cs KRhu0̊]ID'BVlG KWKݛ`^r]G2rlQL4 6 bT s4@9,L{\U"+(hgڝ8t I]z܅f|bFm@X;"S)!l_ZU&3rP*CddoX<9-A*wñd\v x3f."Qζm(cZ ʫ+k澄%^_.8aqeڞcX,h>Yڪ:;YbpP]pWV;?oܚjG/kC?\ٱ2TF] ɝv~$O4p.yUj[Uc8/*\x: R!nPKtA6/) Ŕrj ^id+H4B3y&6X4hbyt/k-)\c2(/hSc{`aLj4cW<5 ~8OܭZM?"<^P))T4iX/Ӿ0,E+~Xhy[]ԴLuPR^10| Js3'`s@ #ԃ7̮t y3ov4f"ˊC\A/ߜt@ͣO̭䬕!|>Q}SVA<9MF+S*|YO|4H=\-{a $ %׾DwL͂Wf}2yjV3,ֿ@=s}|h}mG uR*iV.2.aס\pVDM;!FXY :gO,ߒDI5ٸbkSAZYߜڣ!"4UxSLV;f=|>sR]wd3tG=-NsƇ9ݏre[ge/\i8M@Z&uC.ДzIk ȁn%'ǐ3Q!ܯBTa.i·4:9\3RnaeG`ԚLϸN0YX8eڲ%rV΁+!5V&TźӵUK,# }{ ~GBCRErhAb1d#8V~-|v43W/*Y{_dkO%ؑV0K*f}EwPK%sK凭cP/;d5:!c2q9v v0EG$_jbZy;&*-+w%*ϨQ&ym2'w'JFLn`\q; z_D2nfz4g[v7/AۜU8)Zzt]1AsvGRw6k'"AoecuGC=?GMA_+F?+*Y%QM;bt]K֡q8G8EO5?u752,($ ..d9,n`{/ .D:;h-iͅDqQLqok--w~:dOۣ0:TUeRTRjZƜ@iw_Nc%Vw$T޾RjYhʷK v.6|{M=p1 @^ 991”WtԹl(RcLm`)bAAg| .6ݶJ  Дei#ekYw$ ?Y6]m^2a_Xw}/\ޮ]V~6VYMuicjbieJ wܧC|<`%9B5gB G_" Z2ytDox| ^SÖ=cuG"t;0d*{RWyD:.4m9SKZN 7S/e)j"RjM~U 5-"|x ?v6A}K:fG;D_qѳ/1T3T)>as4ktdEo4 NȽ 5s&0GSG'nӲg;~8EiكP7ƲP&QyU6ѭuU3(E/WJɧ'IH5#+07i汢oQ}@ÌGr}n#oT=YrMЃMZ!Al)tNyzE Ip21*A.L+yH*j@h z\A?(~շ-N@aCpT.z"eCɖҗ&WR3A 19wmQ7f$4և #T'k%{N̬ӛF%=rmexu72t~kevv7ѯj|ҿ2?F^E5pxEPDYŤ8Z?dPv;/K^Ÿ&2_&6@VE.DJ=a^b,/v7>oCS*ۄc?i$6-0A7 KT2BeU%{)-TSpbGtM~H+ rRFox DOӻY@cZT!OV*/rPd-4@Gw1!"W+N5 |sog)CG9󫴇]v?$Jjk 'uw;7)D&LZ-eܐ&=BƷa;IFՠ*< Y]5awR@ #b!VNHMq&gzoBd wA(xtՄSK֥g@Ͼ_03p\76I -f,'$lr@_pZ9 1S,ŬݲфzH_nU \:- W`߱fVSm!VIc&TMEy \*Ԇ V+`75Qw^]j^?θ5! Im˕D.QSϓA,uFzH<fY4.m|rnL$Yh szxWȶ5PF`͵Zm>E F3|^,&p!;BPAZK&tK^Kcg6!$yy"y6<C ҨG 2K3z.6*1SzsU8#g^6x\@{=Ml\4sJIE bkC7̋ߪVZя/+LVb;b,{K?5p4`aX}Bwߛ*59!^ɴl8ISjר?DFVx8g(\WĚqž ioΞ3X3qwΗLp]b2_Mz5|e6%<DRkʌwG QDbΝ^㡕Q:Yr։(R,η6&|.\ǽp͎I%$aЕtͶ0^?ßWHRw\H, T{ӪC">Fq sK+쫪ajgrq5Ae&g CVT9spЈ8{T6[)JH @{4{,H;zzZC6G t/Tڷb_ai)"Sl{*ioNq] O r ؔLۭnU k!baKv~0(V1*S7wcQ EGX{/u:td.ڬރClVk4FKI$/yLS:0<gx1(k̇Kqtj_g{,oJXN/:Yz~B5Ɋ[{nzP{^gS?>:Ʋ8?z:&'νp/ƽVښM BtoCx<@0yq@^dObX|ma%" vtq"IyIò/[nAP;(F@73:k95k$TkqdS*p0Qf\9Hx "]4,3T7iB)Ce.4 iK1hkٳo_+Tqυ/lm5z ZrT+jK#K:JK#`pgqP"~>!6.KtvPLjtҨߟZp4f"u:]Ԩ:]%gz7+irxx]nJP fѫJBlp w^Γ]⏚Wڝ{rXK(!?q0t;耋w^wfs]d a< t$./LSyE)9 ivˊD>!쨶!BmI8?}6)h Vcޣں7D" XZ~Uik_h.e3pe:V"iKV:k6دRl=QU68w9m\UfGoTp#{?A静ܛu3#mݩY .Ρ68ϒ]]pudXL݁B^xӡi~TkA=Y\طCPE"q[ sDz8/woĶI˯ dG(x:>{dou$62 טt8Y7[B#Ӽl@ՃJh_LnN0;Em{8l[0\(@b|)B, e$ba_*pi[(6xk_IN{4 c]lP4|gٍYM~J=RI;N)D.;=N3kweȡ%tLLx֋b}z /jj󴀌RKWrKT ʁlunW\{y&~49pEG9E' ̶̡Qz(  /ܿy5p훆mo6T0_84: "4օ_`-Y]ʏ@:)q7gqks;L-K W)0ilن)F )\E24ഗN @zjiic.P. BAmd 7O*f!idh1`kcdRe(nLɋMv_IdœS>dtfMP!Bߜ#Mod8(o)n_oI }[$_n+r%=oQ]1f +-JV7H(FJ! MƷuث^?R e:((5^e)wHZ"jWA\tom![cw.NA9*ʠ֔W ڔ K4%Z.%+`Ǜ;WqRn:+X>Y_iEgYp*|:zƒ 7$5GFfӯoS'리Ή~r_&26ʨèpaT2kuȄMF{ UQ^HrM6 PG^խ7xZ{cF]֡jGh7jWݷF#\S[[y߾OsNq䝍Z|=~"VgT”~5|_M>ș4 G)ə8 CW0\ojF$$5H&q# Q2'ѣ`ԗO b^->cet/*ϴ$,`%TchNH6Bn4zXjslA!rIKp¿K !bGtAWs1N+ s)1q6NKxTm>u^m1WHJ͸jM}'nk#ʓ(:,eo򚅾xo^諛4BY;7$+#Ѓ2`s [.) ԩr"\縤2}[%& -BoMĆ-'TWTwVP`݊~]yL ÊӖ)?%~UU w&=\t_@pua0j<9=X+ OkvPPy܍cv0%H]lќ52u:}H?ё %&(ll*ŸZ<.ogŒeGp(Ah%\ۢŒ{n`QAM:aJs~`QndQ ^ 7OKċ%e^^.Ćo(rCn]0՛ r;Ai>yUGNDM-B9J]* ~.cڅr7UP41\Lk*җ2h.27OhNh;l uTR ԆpC l |-u@QXW^(^$]^ faIc҈WhiqgiV6qw6_77+–T3~EԆo(rSL;FMRQPʳ|MDieft[Ǚ꾓 ;\\6WrYI}E}V=՝㽜vx'G/Nr/WuQ|N|P>=$fqx_I?~ .f*OuvO}Rh+Ӟ40' R1@^x){yyB 5)j-u1\x0].^>ėw-`+ Fp+d(̄_/ ߊuAG= nNMZhOPY!: $ሇLA+VܴciTzv%UQ'ةY!EtCf q"߈{{z'Gm |j"k0DUHj{5|۪qa6HKx{#nPݺ:p]$ ,v\C!gu&}QZ@2#eNgf?jQ5HNMg*5kx!Ώ.]@RȪ;_^!A-hMC;RuZ[ 񥑐~\j J 5`e(kkdӱ+NCjD؈l5,da5:S,M_XBBzHoo,݌,/XnTp,KFbۚ:Vϻ0HF#DT*=V'hWG[BT' -2)|d1H'眛*-+KFPZY*R vs[izu GT>Uh82C;5_LEǡ"sdx|0>V"r%D+V3ZaapكSSCd@Q7BXzKE F\闐߂¼np8ќ{VWv/>dtFw8y[:sfcH AO==Aː 4T [㿡`@|W#λh3ԫVc8u-N)ay*3eܽM7%ި λɊ)%.] 7-:@pD lwId452{QeɮPkr;q@j,)Ј@0yb8CrN>6 ^$_(i -# MUrPR$Lz R="q|9E%!ЉacSa?1}s_ a*9<=Q+raF84!U 7O*,E[mTabY0fHjnDL\S;UuTP͒D-M\x1dH i`H TeV?"\HUWR;c~ZcIQ4o=ҐQ{#ZaA,O>rFFA68sm0\AxZ٢?xLw˧MJzZr-ӷw4uUPLʋy5OڧwX1l6п|_};%D.!AuБ.ia&)Ss%G{v$13u}k#cTnh3Af57j}NM93{={Ӹ;o4nϔ7Ն'øW" h4SuѸ7C|n0_/C`x2w#U,KD@ 5=^H֜4^w=nY* M0dF[ڪiDERP*N+R ɰkހ+x#jݱ DnGIftܸlQ!O' lX(8Nn`i)rr3_vT$;c; g} 2_X \ߡ{.czoh]R?N{bNbq] ^Ȓ)j},E_AlrD= 8t/f[V }Tϫ?GlBBVLoǵRy9R[i'a^o/κZAwH"B#0Qm( Bc.XWrVza=éM <p@7 ǡGt:R9]|M[030cȖ&KɒQ0H{i|ü*6;XEtW{MLuPM l9\;)\;|kZf%23`E09V /i[>Ayb#;**]Y.Fae[ ٽB3lARgަ.^ > ZI+pXT(5sq"1W9uYP$%p%&ƻ yjH( ȱb#O{ha=RYҙ Aw3˸z+i:5+/$F%8 t8/Qe I@@\cX@",!n/ʊ\9 fY1x_ Oh GbV`n70h A eSG}(}"*^u&`˧>Cvӹ^5Љ-sЬ4slyab51 u{<xO+M'e7jQdA V1%oQݝ&s^H(8TtiTy84o8$[H`}ȫ 5Ukױ*'9~[tgH>}=Wh8jsA {jP2ͨѫv5/-*Et؝ hA,F;ua0WW^I=xbEjGiFPREV Q{ "y,?OsẤ 8D']:SM`$QC+'`*0vՅe7S/^nwCE%K3ezT`ǴbeX`ҡ(K] _Tm,͍6h~]cU!K eTOeY ߊ86 w]e`0 *\6u!)z Gydͻ?8 əsofQyyG7]̿@#V)啹]d۰=D(OkD( GęoW~j<߯]>ԼOՄr1NՒ3Mwot ؅_'wED6}#C|zٺ.lA s*BI"|y e\Q8XM7/8J:&mOmn0 0x4k̙e n YнKP袦ZHIrxN2=腞>!2(酀FB9E2N Q8 <;H(UIHHr!"/z/Ie 'l> 8LG.gEAP V*` =7[4$xv<,d[x#}=g 4Eݞ|М4<] Վ><;=Utn ]3(S2DO?nw*tϜl/ P rr?[RFn;aNTnh@V-& TTY?_.ui$uw3ˎُG2r "3YNCP82X=id-X.˂v;D"?)֎9ԅߥ(KOKN tV%hrL2)=H;t@åP5&D9!q7 P Z9w8zX8㛟0׶)C6ewӖqw"BժË/.j LWV Tjoh>ؿ6C! 4=_ї6;DIoAz$HQWIP?z ҕYy dYE=\P V{܅{Y&A^7&.=:$ExJbK4iNK.,5cMŪ3:!RԬa熭'j6s7Ք +@Qd$U !8 q ,Kwk21+ ׵^_ Έw %3sTۿ| ~G(÷2tNŘٔnU3O0ZZާ.0A&5|d:Zg0K$&01Ved%~p <E[?[I(Zgwdҳ*J[ѧq~wLg+ca_zMy{3ORY  fL}=HJ ?ћ1sÆ }Ro;|C}jNPR!~<#)<Ű:iB׮OJ~Vr6y/vO7dvK_1/_dfSa{)俜Q&55N64ˣYl80s:%6O6CCs0H(r͸&O-Z_\**6Urj)+ b%ԌM8O ^GZ0>F gp84w%B5}몦 Z[nY.- popJ`hifT>ATq5 aQ0m+R79["5 .=i$a^xjg<`nXsg+Цh&627AwH4E"8UqPXnΗGu M.}U/AO^t*>:~GV+~G+Y][.s0,A԰KJjCkKE޲L0߲Nr=:Qռ_X' w_EggIB'N3 (1(2#I7J"C$ ;_1bO' i`rd(Ieʦ^^- tԜO8o{ó_ʡOjL-KҜ8wpT+0|©o zw~ X,pgM'BjU_ 7e!͟H^T":˗R#N^wjN([Q .*:O$NxcnI $yIѰ7nV c@R0׈T /B m}NE<Xʄ sx%ڒ@/\Bmd50pJrSIdWʼkVA+K;n[]mw#^YŭeyT5'Hn*L7M_7_M8f6Uu')bwAY;yhڗ y)5nP`qo㴧|UJ04)Llv*UjNhcMLZ1V__]eph3l)m 8!:sVGeo0zC A'ӄ 0\$Pb*Rd$ =݌W9sTcaRg|̞ +8xߴQd9UW LQMDm)b9}),*69jlcRQؔNlD+ RH@a4;w 쵸oY2ic~q>u.͗x*GAӽ3Lnyd̵h2oiex_<͗s|"s*̧ϥD F'W' }0/*GxndHmJͦMc3}u1li|`4Wǥ'?v݃{%/ z!h(9*@ t[65=mBCP155@5C&;1O̓Y-II&ˆ{vRK%MH2Pw!~`*74|'@ £(co"5uAd`6^N*<1h~|K <qY10$D\{ = z9Sd?L%$0Ul}|M)^koPu%}ث[%3+S>F"c?tBJ)^Ic$ <v o (KI:MFA' PR34uƌ5x!$,7A]8Xl]c+=~Q"vF}1Kf׳ ?}!5K06g 6#"Re cT5KFLs7.j̩ڒV}Drd?Zh){ToUWٌׅjOz`\ iJ%pӂ|baxuTnNO@@t4KWrSk3h .kV`'#5@A`04 [.C<2t@EE紊NaV - o3d 1t,g矽d&>J<{ֹPK6iHYzZ(?ͺW~}&>0[ʘx2o%X}nH(q,aQUjzZ16"Un͘Χ i7HTI5 yS[e6)7rkr{NF54GÃ4npg XXֈq}Ed0M:ÇSXL/lE[bF%EH% P\J.Es !QV塚5(1@v!Yp[htUՖ@%#.+vCQ^@+T٪&sU=h>T_*WXW;0o3&a1T?[!7Rxoh*SY`wr7wJm*\.N80`K{:,s{7KTŅЌ)PSNMW R}W3n#l{'xG=_7%vK}Cb.vliILoIoMq5tԟF;ͬZ5uErh$}\P|{t mVH3g:e0WmV sbsIyuTkpƴvTeKpt!.@NZ󴟒 kqY 0*ŏC{ϣ=%ƒy = ,xz[5VpŬZF#P~\vt}/!mU|1zKp!Rz/q^3U̗^壊ߩŗk\}ںRx6,KgզqV[v5:k#ӂـ}r?TB}98RXjbyt;Sԩ*;&˼(p*6Tk<8*& $T!{Tǃf_I:7T.!(snx 2K@Ze#Etwј3|_Lͭȃa@bx1I͊ݢ YK!$.79^xI*YvO n-"ڿAl1x 3浄Wѧ?ꊲs}(vYAQjZegDg_RCJ|_ QnE-YLN@m[XIX휉T̖FĥaKB)R&JVm9@M33N-$uZUNO_|Sm?Ycmw;Y?}S?Y꽯YeV.|:|+X6Ϲ槳  ~)C_l: Vhç ! |bиҸ쟨z9ɰOk^[ĩ#D8?S N9)ѻtۂBBB^I ws*͛ǗxzZVʐ*{rFҋJh&˅o(΅SY.h +TH' 5|aO=ihQ8t3kt6>Ma[8&t#& ^k,utdfyawzuW*vXXLZD>g֨kYR\g96. 93hM؂RD[VKlS(_(G+ܷF*0p\ߗ]UNAdI^18dU@к81d ۩Q%o1Crmmk?BT6 bBS]o,I0z!Z51a\+]&فlrsŨ1 Lնhf>e)nXSJ$yݗVLܭ6Mh n~/Hk(,@uar7 Xר5߳Verڌ]y}Qh|KcNTB/ɺgVqi!B8D =h;^^zZfB͎F>΂ >mN҄R;TaՕ87hMKzݜ%nΩ`FQuUr;˝syezt'EܛC m+YJxRg&_y1Zі<*1wfDd-R>AټLIgߘ5;^`Q4M2"V=?-ue> E=tz[g8/pnxɧ X`a/\ۚG10ͮ-<7iXD͸c0P9&ڬ&D׼g ̻x /:7muS.дr(՟=7=(@UhY%]y*B~&C'Q }1Hq&1ks! n{pUG9ԇAxv"B*P- 'eE/@yg7^%[W»ɣaCTrVL*JaG)첝9 rHZ!]H%θ h*d p`A܇BaC_XheS+3>'HVg(y/>@ݙL 6O.ھoaTdڕ|%8gZơsP%g0h~uzt9 uSͫ• Ҙұ+HuX6BLO#h){_B7@Bz;^)L;NEاi9m48,.K ~Uͯ͵T Q7?_ʔR| G0*̃0M MՑ>7 5u~f\9?N8# !W ktq5M%Nc1Gp5L׀%gxԞXrQkzgIQc6FqrmԢ Ej=pqVwfkIumq_!}or t X35N*T (: LƼթxՀb=$_dUxFоjƝ p`]ihn2CB`m} 4b#[A,x06M}@ZpUH5|19x5 ϮCMd[歉*c>B5j8 Tl 9uIVV`~08pHU_ zgfO7%O3t hY;&z>VԂbS']H5h,냳bHkᰨ!R]?}<A(L}ŲÂmu9-LA`U[(=xǩ;z)c£m 6%L*La{z0W{NB.kH UEA`Itxk;1J>`Rt2K{N]=9]r  '?޴w2G:ݾF,w%P/6 ̓"KîE(zT ^xԠfIPɲvw3sTor>EüH`~'|+͔#(6X튾jca XJ LT{XVːNCj[yj涿ϑJV.^爨006\xvcq^<:Rnw3W`ԇwQxkNGh6hUga\jkHXƖܫu ~Z|'bԂ+O#aҭw&G>ENM?=P̣/fX,5sޣX"([]!A7E]Yo">jO82n0wac]")m%Y:v#y2OE]eQpxVd *Ftt;$QEn >5)%Бpl:#gӤ&sofmEhΈe^uNL)҅t#gcO4vҹM3݉¦04QB e)k$H5ڃ#eO}P-9>u$/>#&SE~?< R %^}YcsMa*{P@98u`XڔcrHEo%:\ $!ĸhJDe X%.r6\~/&SϏ` ]eNE4*pa8džD'k'A@]4),^![uEbEAC 5@땞#'l7$[^\ݝ$X $-cTseȼdVsY+qѠc{E[t; ^ܨj_K^^\0JM(0倈Rʃ__Ֆ`}hC;/dGֈVi;@isWbǐ3ST # `*6hq ._SO]Qg:lcs=|Fx)v0`lto,%`:(zB3gr@~m8znmuef0Sʠ6owj!X#:s<۳= ڦ \C_ݶ[nz2:HCc=j05U^ ܓݑu.uY-iqRF+϶pxoA`dz{#' LSUd66#>aߌIN-R#ؠ8C +RxMVcCVnd/{(GWqP;45XuAnUyE쬠nύgٴ)!hدt:Jd\Պ^ o'S]F{PMve} 41v?w54|\YzQB!j?c?i+ɇ̹t|qysd+e)/(%KM-B[F} rWFm(Nl槜U,#DPTwc}d%< .T":Ϭt<* F +*0j-3o黵LCRh6Rk꽒.\;qo@;wnROAQwtopP00x9^<z6r*D+b̙0c䱀V sn<,gVt˩aDsg唶f1F?kћ7^c*tjzY@8vWf OG*Hp'^'h4=EZP fDZ?~Ȋ(A(ZA~- 􁬦 yel-vHT "B,~,vSKc}^ q\kDQYB|t5zeg!$xhNUdBrR=~즘4XP,G$7e߳ԅ6 T Hz܀Vi!Ě~)"Yơ\,^!_ѽ_IoA§Bc`21ho~P5i!~.!Wj){/ vMU=2ɍO _j+_5ə}(W¨'W<ņ gԉKhC-mIg/s[>crY˽ ô(oٗ[,;|_?ᨳXgEx4_U6qr0黩HI`6 42d[v`JY"\B9*^0܁I >VHuTn'xiYv  dM𴗥: xQw=1 mlJas.| {Kc9^;AN(=ꯞ]*\ssp~~c&OF2 :5D4KKlI=x`F\Fcyl$ l.Z/ý5#UHL-eI;%d ĿNĖ:Jwn* Z=e>!œLp}{O͘1LO!;ޠ6FCb֑9qzS=^MU_w<8ɑPu `mEa ,2}ML`i8SCw UC+Jؠkq18&(٤awNwÃaRG ~&=c<_tB1 `4t7ǬJhng"ٮ5|eJ&sO_PIq=rfOΝAR0Y J=zケݶ)eK}]{뛇Nk7CӮ'OT%ub|q\"* yB<,Yrמ"&'\ }BI лRY{:*<CY7Tߖ7=]P8{}ra@(Ԇ/N֪y.riAœfU-L0^ (sb};GN尢=| 0 @ǹ@rRҤo (ÒA~$JM`&M Aя?gi5|V,[3qw&Vv*QKp^#a WǨSa,r'e ٭#5ݙd Q1 ׹U<[ѣTJt>OL#/j}[)fyA:g~J-,O0Z8~ё:ˮژK:DG@](*1lm7>m_)QʱD!fzfɫRE .W;MttXڜE< YCPHAY#G"/ti못_]J^Oã-[D58dR$͹{Uiz`GU0L~vR5hw'v=?wt4dqЮ(;IW\o^yEUnoU˹X:umLTB46BJ;{S"T)e0v<+0?ew$S]JqkEI$[lCInneYec/U|'5J%#5F2dͷyuN1EZl5iHم%p a&z@Vk[a N : 1L>P]i0kd=݊czyXQ K|Uuxy2{^v ",&YEek)` qsz8xz~-s$ Ҋú_U[>.h_|{ GВ%ߓhQ*!|~z A:aC8FU2g&% bs@U ~-_nʗcr\.{QOg,!z ܗm\?UK3w}7l_|5"[Fvq2>6Gԫ[pXxL;K:GτdZt'¦ VDuS{|+WZ-gv#[GC:(901w]{ՂUG705ZĔySvޡ<̼RȐʟt eqMTQFT :1pxNp+k2qϵ!%ؙxG`8!U?3 =i(zI7>FL$dnܢITtJ='?'`/[l[{io BT$Ӡ0EXrFZf~5BRGZӳ֟Tڏ%b 紏 dCKEّGx6*%?0{ VąHvLZ-qxB<:RI 4 =t:D=VUtOu2]?jfrmgcQ5I 9  ;v !:*~σ]C~ $IdkoMi}1(R0B#{ pZ~?qlv{[:䡰UϭξV1prtE&saMtIMY8$zvU;,8\H :A؏($igjuZ@{@K0V֊^ķޙqs9ޜMfxߟc|']}c0 CZU=)VcwMfV|M/s(ɦ@ꃮEЃ&殆4{J̉EyF ǨPe t޻J4A!Sw^$@w_4mYg[y9,eeX3GeUግ>dڟA#S&~ȖdM8'm,$r*7?B_7Z0&Bw'YwhsnzlXK";O5⅑lwo$ w\$`[xwsD׌pB%Xz3;WO tdnﴵMiwIxR5eCF}x<&IS<(zZkn*ќ TuԼBZ WPV!x тJ -ۈi#.Q(\f m U X.4Ql1rbk10$|K -V}C뻄?p1 b">F5mCWaVX3J-_+ta+4^d룏Ir%Hr4OI}6 0vJ*@NSv=e0wɸP Gִrt^j-]@U{Αelԩ!\ H^emqW+lj^4q eGZ. DIiQW| 9nZV?̔(F+H+U;(dk~< +q#Fſ 8Ku#t7n`TU;qc}&BR`PZ='5$#j3$$_aʒp}'briM`/ ߎ[B]kofNexXPQ*7RB9@ ,s"BUJ > AeO`֢({vA`EZua0qݿ ipt}t.6ڽN_qWIב> ,}Ĕ4äX|~,iHDwĂ{~4/ʸSkxqq!-՚UR7+.aD Rm9t)3@}s"WX\E9_ 3 $g^& hp®qY-!o S3,7#F{wb[6鎱j8"kPljyu%T- W6kpW{Y8):A(UCkAqRI$k%CH#REqAhɢa:jxXEqx?dz`: ӝok 6/,Vީ^4|X.0pj#Ee@< h7ct/2m W̼4yl\Flq]0#tZPYv29CpH?#٫9p.Dj~vaQ%9˭򭬁ņ$C rkw:kpe3GjVߠ ^9ų}"y9 x*Z'a{/b|EBe&Ve&cWweMЬl)wKK\Aۈ`m&a|"[KC?]d:4@)?\`DY kH _r{w^}TGqǕ08q/Oٳ-i eaGnvFuzr3㟭7дե u֝DoH#wit3y9,hL}nB3bylq Y:!OCEzVGl0%o )(gXنuC,'Z!^Z^k_9A (5c12Ƀn'&j9N*%J J[fNR/Ѡ0,=  ԅ u^%=(|Q_-wYu2qYRe ÃJ~iV@ Ml+tJiyuR{)vX$(yE0rTԕ$ jW; gjI$]ry=54xJzWN),w֓ӱMxoc_{ׅ$I$w\'NN4idd^.I˥C:eїY :&>`VwteC c)OuNCatqg#?|5d0.(S0>y>.gT?WGg+~*ngc7z yFDԉE 8rjiL՝ Ƥ3j0,Γt\7`K_,Zx>K5=Μ;ۭx[Y4+oҏ yxMhBFޘ\3y 0D|h2)amzH3%Z:W@o.0 vbx҈uf8 Uq/d|E+_6CN:/./'67 nʺ!m#O DA-r3ϕ>j}((zUBfnzY.w-;zF/t-0#mE>++%>v:}z~,'MOAִgf$({K3`x99dx3x~]P!pa%lʎ{Үt\'Ưtx?펼ӓ/ZЧ:~ (N¿ʊ/mvb$YBX{u1S'E p Fv38J! *cY%PkӒʯ$M8P۪Eӗzζta6{b.빛\3: Aei1ZQ]dc. "FN_vҺz>lqA;VH4kQ˄4xO7–&~a$W$`AjD6qv{忻+vV3V% sqtNz'i"mY|ͦ7aF~J:B> m9CR& N,_uEȾ/+U0kv{F8Y Z5HJotf09u4!"lbMO[N3+AGdi*ٻ%IL zN>)YQ M \7$$4_=S:Wt'9r`Rf|lpAלRϗo~:VX7>̇ð֖1 17˚%A^%Kmf,/5:w4ݲ.e5='ÌpSk\$ IT[?媒pYU;SlK9:qEhCk 7Ts{2ڦH}'ԍWne*a3k<Ll.>;IX![el|u\2t?՛>*j;?k\WI.S q}EHe3ב~ҫ(5 [M# H&_]Pp 6m`yc!Y&8rz*P'~"oŹrφa3Qm~I`^ƍW ̀;ѼMNQ >ݺZ=9SYIb#V pebC n;qcUW!ZA\d뀅/=l/5=ڪ;]9X x^ju]}(mࡅb& ZRI}2V#5'"Ee͋"Wb͎vڞZ'W]Af3uή*hYL!׏Ir jEMf \꽓n'Դ/aZ<[Tߧ, tK}oO16`#|+Xv01[*K\ţ@Dk.Oa!u#Um#B̶ܳO;1.m5pOABtJ u}"zn-(oFTWz!ARN"@g7ESۣ D Òk-} Bp^U_[Wqb+:[sl92K8`t۸$c9]c vo0b6m6eئKVbBYrj+dXW|dI#=*AeW-)u0̌a:APFVZɉ 36د}Ma7Ew=(ېgAERzH+Pā+.^2XKϖMh%JcM k'YH#X%em^\Kg]s&g=;p(6œQ\X~eaĸ6J<61ɟ/Aヸ 8L!$܃{m[|d~[7h2ݨ;h+ hpѨMopH1^n㺷E!c9fp"ʰPѷreYo䬓eCN}9FD L8FJG@T0hBw3k bBliS;^y{39ŜY&ݖǂ-gRy#zXT(:,gT B'z}Ɲ,B.K= Z#z F[xlGzeƇu6@Fv'3f'^&2cX#TڮP(?  BO >Is5&-#i-ڥ{NZ;-֔/4>ʏ**xx=7ǚ^6ޠրk|x<:!θBl eL'MF!iV6 NzF ahsaYNZ5.OSᮖ`-Y&xT&stKMџ"Xꗌiϓp2iRAa8p'wV2~)(sMLܰ{֝L/m蜥Uׅބar@k <Yq|҇c|㗗g(h "o<Wv%~H0#Q Xk錒^ '~=Z_3/ y{z4øAq|ٝs s\˨y{1Ǐeq!CMTDѠcSBW9o p3Ybw P5#I~Z_þ6ѣ2(w'9 |V3_N*[nSUg*.4}Uxyʛ(\:!-""JWL.`~@8`?cV'~NHO A)MNz$j];"'Fm 5JS% QX x(WBaI0-.\BV-!-utw;FJ S9>E! 0lж6A:VBaG\Nۆ@ 4o٪=@([RfM44i`<#eHN:vWP'wB9#t c }CSzJ Ԟw{/J#/5L@ UAw⣯29LGEWilJ/(0 #b /LՉ NɧϘ%gJ"shaH TV$hH4\76ou\b-=P*ש[aij 4rE %`nu!za-:MHJPtYG=c"ݹП*RRɕ$q*բW<|0R"{/g6eTD"^Mۖϫ0&,yϫxsahDoHqMe +BI!Eh@UUƸ\.s۝ʁ⧀2q@ S]AzS8D?yRGW_y⬉ؓ*[k:-=IմGU rSp%A%!~tT)X+b"Z=@P'aL Iȵ}zE(I)ǷņAWc5+owq! #?g3W2E:Q0; ߪ%L=KhC| );W:sŤp1B᲌~b$8DN";0m4wppKğ$m #?Suف/}" F$E:|?QtܠG줰rT}$XpOv#u@T,WrS6k>6/Cs.d' K%K"uGK&03 gTm1>^nI2XQ̺ fj -^Cg`=ֲ%${|w׷#aϡϵ{_zG m%!v/~LVpOr*\2'(7iQV<;ŮɋLuË pIO} zh;2n>^"oNF#YhsaCt0~{RV$!NqC5\ adX8_9oʄM{O*?eQRTio?;<,ˊ N8-̶oڻa}6I8q2 itS%?\J^r6nʼnvz M&7j7,]+1-@fUk jRf ajs -4ޔ+65Nj .$K-mVpL+a0K]k:ӁT#qŠfY3W8z6a|) 5è-+@vbuHhA01 Ui,|2|;u8/heEa[=!3!`$4vӥihp&Z;:T}$9R`VѣBU |? `r 4zk'TJ/$$M!]gyƣuJ*iaϰZ+:IoRZ]}9.z8rYf2UfgQ_WX_ }/%o/xX8Ago;2%ՊjU!iJvNV\m=ڬƿ`Fw|PK;adh,Nol' _U"ܻGMʈ'4`(5X P GB#H"a=,bco4;dub05׋Ⲝ|>gG:FBNUV,0gNEQEJ"F$yͧlFoL#) 8_"ǧmC8MX>BAn <탈؎bwex(?Ha (嵚{  *1l$cWkCJ(0UrNVJH6mm=KQ  H:PZ-*IqFH;9?j,~"XWU)%܌i?T慚wPi Uags qp.,ܔ5.xAd8!͎aP[؋d4\YIyيm`A(7#nИ~AhYƄi0.V_|Ji[z$2*~;1w:lf+)ygqt)QPxS46|GMjܧGҌ$?r5vYkBMtY^6JTqbZ X@-{ IL3Ñ RRiB+bJj[<՝S(hpz=Y=I8fM fI5LH}LjhRUa<[:NXs+Jz-6J\6.fУBWBչ?i3N34hq!^HbHsY)`Y}qۢp15S!˵,AMDtH`S!!Q6#O/ гZ+0dzXpRnnj p6w*6:3gjPzhNsJ}\W!DIkO{*^͑m\G9clLV%(by us i@X| ’JVal~9氆b}ӃO@}+4މҐ4B ӣeӖCZunTapP[ b8rK Ͷ*  G56{B(*¶ۻ8ZljP$?r38 "69C9$!d@= *ii*YzgŃ.GPgʟNaE݂G4 TܽǺOtLqtƊtwk<ΦAR2q7\úm}kھo-&N07w'`/31M&YGݻUGßrBuv;Υ0 붦;-bϮ3Ix:vuS|\Fsmw{?>=.<8Vu̾W(r}9}TRzӣԍ{4.gq3a6"{s-Gzop׻Me >,RYB}Rʍ0 i׃;ok(~mn {5d![￾~.Sd.t\WUw}->_&?_>>|}oOe{ lwU~w?_z2Wf{>JEr_'R'*_zԻTd9]NnM h~~r*|t2w>z Γ+y!3^}v_/Ɍ~N@?^V8|z7נ󹌓fK.2@gĔ/pvv~<I!ɩ Gfc8?!sZf/6i{vw2?Ag23/~gt3\ɒitu?L' 'Wp}:ߧnpr|z2M5'~4~ ^E!|zT:AҚ|ZC"h@:/;x1XhaX}R0`%WB,$vCwT]Vx/ojm^' ZmspP8F=`@mP(z5>Zn'4G e }t.AF[;&>SLzD5ƬZ]*e]ݷojB{r֮GvrqL"` 2Zi[/{@T*CJdEUˠ#׆JEc@^vc!A"#ItÔ)Gymζ_%9]j$a;1os]uʨŇcr=[k7D]}=:d ٥Hroo}pԞyx 4*g}DͿγ)|+L1mꂔ~ 7k}zEs/q::ietnmC}JRR-2϶gc,5<2)|JԕJT{^6Y W<_Ps:.)\iA4hW:<Vw~PTYZNq`0T,jqz;ӪV\oAz9,rk!+]!l\3q5Bq$'C±* N6*IR>HӦ%.ۮ=t -7bPcJ=)V AE#s`^v)N_:$ڌ nBJE|ϴ(LHEԻ&1ү'Ap06={)*" ~: I;qaw iB1MӘNq6_H\HXH[јԹ\JgCuoiNqؘhi*n\7 iCZz!~adžԘn {tl4whL9w}hM|7r2n{D4ͩ/"ſƩ h2je*[b:Goid-а3M w*^[:y A>O}s.ؚ{K- ?yM6{^kUNb?Wʸs:{'GL~FUMjN-wy,a+Q%O'Z҅5 E*F IͲ%?{; Vʅ̍OÈ/'[?y=UY5x.\校9[ l0=kS/G|-֤A:aTm=lBzdDFM;L=<qU#;=8e ޻nr9 _HV'ÛiOGQnz8Cv |' %`"ĽLV9>Sq}O7evo 7O=u^XI1-e%[Ig2=-0o-D9@7Ha] DBz!9Md:6 pȳEǑ[CΏr9Pً!:')(%艁UFRGͳ%LOb-OQ(0 COtWY *GYu[<6Z𲗡˦ Z"t!1mW@œa8Wp^"%*>KeNG aٹ1'h]wq=_PHvt_{2"8{y֧_gd@91 0 LJU%I7 *J"F>IlVQ5c?e"Ќ#vLq-#fMb+ e7p]6v;ݛNy bg_g ᥶$ ZrT=#2KD `bKuJeAkag~ko^~{CU{ۥ.ԧ:y~;o c]]i{tw'ɝ}&Oo)l_+q2$N9eNtgR X*y`XV/I.K$R(zH+s"۳0SA¤lDez()->f3"qY-_+`ëAe 7Vkc1}+Ui(>ݬ5osrs%e?diEfJ䍭Ai}x 50Eg͠c#9VtCzv;(̖`&·W4 s30h\4_mW6E~E qyZNND^GkOo('&:Q>%GNRK7[P[5/=?2<{IUadL&c)/Q -Hǘ5ԌvxdЭln"h3QcWobm 挿:e3v"_ Ag'J).Y>YྖZו˭';SؾN,uPǚbkb뾎25 nGM7_p>O`3堩 Q!OqTbK4:=*Nq!k>)EH p6lbsaWMl33y0i ?)v~%SCk[Ȏh A/M`,8__veo9qp-£mζ;ܖp-- 鼅l<ibM&7³7pqM8^[̌hp:QYw}M7A lO iLZ3iOGq׉;iAXI%vm@;'U˵q\Vcj1iѶpnm ψrwqN.ZIpXBj{:nKNk naJo+t$ɤFD7._H жAݎ{ƕ#;"Jd5=֣Y"/,*YZ#qkWq7<|4Za#!&KyM I'=R:~"S9TZ ?a[6'FS~wPDvUgP {&'rb ?9IssB!.+_2.eVy,`鸐=} t_&BR=%Ee~a Ru eB8Y:aH#:KC~@@DS\yCˏNJͼL[9 qM s$kb8K:@3W[|$uEuㆵ^M;tEKyd^o|dm^,ܶxH騼b2?x !aoǫYOgos,9LJPh{}Kkx#{|eB+xBC-#4 :_DEp=b-NH4' 7:]FuF8\k:b z V@avyh#3|=  ljT_MF.*Kctp ޿ }Kmr,hvۆR4fmYtk(:پSƈ>@]94:31")-|`{ Va5ݥ"q򾼮aS|n b^[TiKY/-2-drCUzb}3E})oT>2N/zK[.ϚdgVHO^?[ uoj)TKLA| W t\1!wM:IB od2 8jPrJ׼->˓z0bXB S'jcӾY26c[]qDMŰ=^B^&SLasԎfJL׫#ۚ"U`e%: Qj&~ZS8n>hi/3y1{AE\XAh &?Y<6ߛ:AP<2ɝ[aa?ҝ tltL7WR(Y:3XrU%S"\?O'5ۀU ,72P8q%{=vqm:TfSXxosIT < +9#*u.U?#JkMګwt²j*98C-\h, [KZ0UrPD7x<к=7;nU2ntɡG% 8"qc|GZJR95]mmB2JZXA4.U@ӡ$o]e<5\Qmc\cVjyk7[JmE]ҿI/23ٔ{y'́N+aXvot[Ǘڜ+2By}6?^Tʳ7N+Yi.hJUσx ,cy1hURל~W֩)]Q6UZҽV1eC|n'Cp3yMfkzf:g[bi-0̹M#s3D-k3 C}Nay0%AH{Fвk C% +m=IUޣA.8*/꼸^ j4x7$*O^@xUi g1ਹGcrWO`,"1Dڗ!GGt]yHT5~Uʐ ?Km*2+];1'~\OCCh"sc<bH0LYv iutaG0?xsZ{z8+~uҺ2GU0jMaCۏ!NBo#XG2BmQFV ?)E0< ’.K~e2~)cs4~7sE)]%Qa0Q~[]|R#+pT>M>./ +NUlɔcQ[:׾C,蔵MqpU0qaCWT.LˆdkdѲeGk,#TpүpTR+QeqF.K>.p=ڌX6& bψhxX#'HV֭ rT%]vN<5sYԇmwRK>1ljr%.X-2o#h9xQ eQ)\^+, b~F"VBY2p./Qre#WHLjJqG FԌm&FawB`L ;N/ E}шi6;Ŝa7 M \(R,੪-p,5Q; ,?K`c[&7Ӣ(4P q Wf#j'j,=ډfDCT14 /=4S`};vx/Ѹ%R6wA1z{*ƧadSEvaij U4?m4$)ukb`M) QBoQV߯q&au/{b+_Jbë$V$Sj*˰V(Z^C¾'1܅Tߺ6F\1˔o%[2,LQH<'YBȯc<#6WyvȥsAzKLK! o=i!cUQM Џ1Ǡ%̑9{u!|$maA[yq&F+|+|<i'8/fvIʙ>Hu)ߺcY\CIFR @˱9VG,M_6P}[Ze^l2 1r/!UB\aŴ}- GݶJ[}8'qy(hFG髂?U',X=<" Lz_GH {7WO0 pFnn vh;?@Qu a0^GeUY냇Dk >c+v:6GF'#-n(ݏ'O%AvbF . 3 /U蛑\am[;r^z页g@Rl8JtA`89R8_̟рr=`UĚa p-Zn3q f*QK`ǽX@GQ mUcpL՞S.&ҥ:\ֹRA%[_%P"Iͥ>#TuBP>~oYI7Ujړu| jR-dZ>K\pB3aG='-`jm[Y0e 0XfmņlX理ӂOFFrQ= (ELV`WuDRȍ*PgZTk UD4N^ҏ \5aWQ/s8/u%K*m% `eM6g^=P))ɲ*YX }kN9A8&;cFx:O]Lg]L]B9pfR * n8(N$at(uȎ3NBm(7nWFb',ɶhjQr@dR HQ}J,T< 5xdgkUt5n'+{'k+bgJ͊8:%`qۡh*-[x,i|iS#G瓬Q Gt`- ng K4?Z;nEvΫ?no L`.z݈EQ8Z4;a&Dq~x˄f݈ a#Ni!yV;_M9@vJ^6="j3xAMiIjm Grbdɖm/UbS=v2O31^3+c2jXg|GY3Mi}m*;PXVUඇo?b*p8]PO#/ۯĭ&]݄\ <y ۶}8]:|vt<ˁ wW2%]p&J޺׈z,B"d$y"0f' F7R$H׸^U#L435/.Fհ'52I$Qf^t/8u]K, @zZZfOLX[R"9SdZY\Jƛz㿹lSWQ$ZP֤VxFK*FMEң6kr退NJ\L=ݽUêϏX͍25qXB_75XS'ZI⢸jlv0]c+~L~ӻ a *SN1=nEJte)`iZJwg2EIȚR4wyTD8LS*{[F-:ؾj1坮!2ؚ.b TnN)MeGba'O5\;vImoJ3B|N]K 6 ў4hSzX~_]cSڲUN?<&2g-fpg"E "$zNz?{xlN5lMgn [oE#ڻO Lj#.p4K$Ta ~ ;0C7A? u)ERLf&Ne3C9 >49Su2 `jY2H}p;ST8P0^\#ʙyWpzd/la`gx HQy@).ƧKi(G~?0ޖ ΫL&3DBr b1fق4!Az`fdS$_}6N`^hН@&Ű5'7~Ǫ`i!0)խvB_^7nWq̭;LۅsZ)$n?V0U[-K$ރHXQt=^`*m| yJ3i)$}:u׳Gwh'=> (&ٽX7BO5y=`!J6췦a#Ygs*N~a+E9Uz #YĦQ-dG"t] 8 Vi8ڲjdӾx xt S@>'Y"*Ѷۄ˰Ki*rD-ba"Ÿ$OP_?^pq)&h3pݹMwL N=zԴvDZ0kzƒrFVtty'(ޯǀ\5@hKU\r AI><]x!GzQ1nZ= g8PhM%0[]öʍ멒6W]5))oF;>^7vPDq;i ":ݧ^T#|XLd8M_xnoY-,tI2s= ?iT&<9| #ɥA13] [PYRtnIi)<+ N/]S@v8F\ڄcAl_h]asp6. y./@sXw.Xz9Rܘ1 g[z{.pI]8 _7 |n!ƪ󭆰vuǰnRATyehR*%Vk5Flb%o$_J,N0 $P Y>`6!r{^ᘡ?*c9Oܠ]$[x88"x6 _Kw̳F :2)!ƷRȠvs9楐>ýp)L(ݔ%⏷G G_F(!a mK{N9(؈PGE4_rI(;b# h+L&+Zjc.F&5=Woz"\ޭ!$FpK=ûx¸y|pƜ>u p[k/j0>ZڂG}F>8װNF)d%(>TKk*+}3]TB;gd<1n4f1N]Ay_LNx26D?laL!nG09.4ij?`usvLo4_I{[jVg ڼ"8hۅ*4DFC29.Dê|[͘oWaְߪTg*FLbgX 8@Gwp&3w3TN:_Td*K4ށ@kW1%;,~!f$W*f؝VP),>䗨dst"s̗w9d[AlHgY=aOt3~*};>R XtWT"RQVl8`- GzZDRybԅʕ$?. *?L/Ŧ\df)!_,.5ᗆ*Q YCA_KJl| (- t7| v !p9J\kj Y"|T^]Ap+IG ؏D6ed"idk#N7Z*c$e(ÎGzSOЎo+sX{˖0K2PƱ}H a_Bhpa}6+Jj({tʌxCV6O,2 .ѹb2(epqawр! G_s3\r)tIHZP[,3;tfU+Ӽ%K{!.+ZXM\-touz݈gyu$HDL8qjՔ69K8WZi ZU *|5ZY(֦;%Ž:n!Nj/ibӇUX w$2 )C36(Jɐ`Mzmp ; z~n>,NebͲ)= ,&p>jx;wMnsfЍ`Ld*EsST$Xl/VeZH65W$,u ~d烓`d{{N΃#TN /1Q`2NuPIػVq4C j%Zo Ƭ*jVz3?b6dp8ѦJ!`qiʸs$uo=t`e/ -C:XN Q=tOLe$WLf!-tATY1؞NMtaD7A2ЗeàᲱ 34I ǻt7W7|K]סӢRA߃dd7r|uz9* =v\(MutB0.ZVrNayS[4+% &gr >?=+̘hv%nVBGZ٭\H7$bmiYnHyJ┫֊TEPh޼%e!&3doRpLO1`pIx Cp/E/on/i j{\P0mCR=o^z^q=DUvJj"KyEE̡)C'(y|sAFN6/f8H*1'֧^}P'k4ehq{6/dWn=\'s >$SIUwY@_ui PW!{8WZ@,79`dD1 }=|Q.@d댷yuI'Bʛ_(B\sMmlt{T@ \uW\m=2 Vh)]qnS%?x{]' ܄SYYBE}P 2o`}3"KsCqAwO4|r%:ݒ(]6y Tush,8hhs*-L/Bm xuIJt:ٯ̀2=a`LH]`J4{QaU5 HSP  (3 Ʌ59*f67"QMPeӃ[giĩ78}gu_8!sZj)tQOɚTfTz܉eJX.Go;kʻ͕6ߚ#'gu}eUd 5*K-y:b3NB~a'*ޕrfXtфeo.Ukf}Og JIܦJݱDJʎK؊eMN[Ak{v~T= %"0ݖ [Edz_0Bq> ..d*mR[B8O0w_Dߴ V4Y^S])(44wSp#wꜵ+R?9og 䦗/;v~`%0iSs}6'lIgnsҋ)lVxu27wW$CkgUMi{)ޜq_Iӂ?Vq\`)]] :bDo3!b*7щT`*6Ͻo/7fWCN q~4v0m fȵ7fhCh-}sS 0!%+BMpyS, /ӭj)P,l W}? Jo]teMEcVVmp&ԣ`[P>tg O#o!(X̻s[<˜phq+Q "{R#D3>owdD5{VZ!o=f$H$PNmD9(ԥ+tI e#*軄YLX4R2O|?FA|ӄB$wzN(TCѱ=v?|1SApO7JakK5 t~[^Z4cf6k.\q޸99%dv 4v9_ûIRO:{@.#A 8)EfV^ wt:wӆA r&ؕ[\>1Ei_vb ISĸ5M喩J-O'ݵNdIl( )OS;= Jfw(t3vSRh$Tl{v?xX_=Z&TS|;m[Smk⿠U$;A[]t뵶|]kl)Pn#_(M;B:S2?.P'Ec WfC(JpP²g }bZilA^IJ}Mq&79e+>ZyS׾֙ ]r*`58o::dA= -mrdw5X{ rsQ؃f:v5h[ l;tYj X Pfꡡdf i0>RexXFVhDQ"AV%-{/-kTG)m䓯7/ S*Ԡ#ᤄ1͍%ϫ0J2*;}mk unPL;%O$zt3pA !A_<63LBN2_8cBP6tU/4 M=d:X4ȲZv)el^UR ˳+U(g=K#rKcGU+rVg3CL ٴK`9i NWeqVV.voP?V3猂/B#/N`a! Lڐ`ԺXw磮5|nWHpp\CvÑeeE˵CYv{;;$Qy3 WZ7>rxψB[ Ю1Շu*!REu>IgESsb+o42>w <0u|U: +!5MRz cw(]钬28YS} ⒎ V܆qQmwP$Q:rQ<_p`+Im۳x:$So5 ˷y҃&{k{ceJqez<鯮(]8o ^03ΨVaľS @E 9} iXאځabYRiL'Pb̥_?| &5`A]T8 Aퟥ #v_ @A{sW_-i婠y)isw_N~ob~ƿ˴72AM1ڃ9fӒIc nlt%Owr/JƏkMqs}/M'Ԣ{JɠTt:/u(M3+sD e{TC>UuwD _eN9u-b.ncOndڽ_lzН}Qҥ KF{(@J{^)~Q^uk6SV tcr;g]Dώړ5x,ab\l셈VxyT.l"o87 @/ſI( )_3 ^m-MU1Z>KFroCz*Zm>) }[>/OFL8G3`2!Y3xV瓾Lbr-1ʱ9~{vK^pA/TyU`hCck}Z>-epFi,v!۞TJmШs\UԀZ>_eZa_S}, 8:4!/+LnO7BU:Q':? Xc }hxKff%ZEެޢw":Ld<@’a'P cpM_ϏF͗"OwMm|yHz^/W3(t/ yifϝۯ_^%-6,Iuoc3~gf(0!F=T2O XxoJKBjEfoӧ |o %`ˑV,seaYe""B7o\ xb ;`=ojp02/%oT0S8D2[`1CeW;!(;z,Ω8/lީ8et4auԫ4 [ڍ̜S*؅ Ekҧ=^:ݧA"tsy$v^huvީSJ mDZUAf\ƭ)O%-]'󆓽 ,MX&lm4EsunZGS:מ/W#MMsJ>F[`$B*+ ;݅IRpB:YFĀ 4~|sO&}GdКEUg(Jc*fSn2>iȢO;p+x˶YƺM2]I!"c!Nj&'5)D.&i7|f27g2 -r` tp2tRF%Qxx}[~Ҋ[cE4&J:FJm;zVtEPMPz38VDxU{D}ĐĬ8ږC]"yPc" |KxoR|},LW#]"k [MF}A(46{h `u2)ޘz_] Ee{kwQSm-e} ۔\+pT`˰$SVv,d$]_ ==V# ;bbܗ8&ryNqY `YX?rvɽ)Aj={g%Ꞧ<îE! C8 =OqZ@&a#'./"`w) ᕞ S<8v2 _QV7C)xxYi!zng U#FiӜ!p@~+:cBKk!HD=Δ bANmf;uQAX"8lS%f.1kKЬJgsCkxOLj%@lu2i30`}>GbMSVVmPg*+!&\s6ZbbbH^XZL<6ڋv$[dyʑS:3?xv \H ]nb~%5J>EI[ TӆBJK e9V%6CmL`Dlu; XPMi/dwr8VJ ? dخخlË|Q:3=ѷ5֕ ǝ;n~1h3ŎSp@Oo~>'J.ȟ^>pZb; ހ2\,28ݤ_ g  &[n ECOb>Nm}ruXFgZ׆(:f&URDBH,vv:qXA?|IJސ+|ۤ,j^:I_msUd}w`ӣѣ:#LnLEL LI4)u4 V|i"UKTwٵt*iS^36!b4eX1(%FKZ8*lگW";IUgּ[zJ -Rps ՘۫1|՘'i6Pme t ,f2%f)B/~?;YBT09FDeF+/,rBH2sL$=&|]nw/N8H$烋z_tbMigsqrK~X$C>$/TL<[2}Juʆ^l}HpWSŁl\¾%횉P3Q0녆}X#BrvKtssKRtp5yH֏ ~8kQg=r +evb!C7!驢j)Kg Sߞob0HeAdpKLijwJP[`o'l zqDDN:ʞ2cux^0}u5Hx%黎=bVWOk9ߦ-x҅Ya(2,)szA1TR h64!G3 'MM ud јKx HU>G)H%Lo||=m3}#2ScK~RcKJi}:+ӆ~*s< 0؋>]kEa9*V~.v1`UJZhZݏFKUnY φ{UQҹXuz/˗ymFT8 !h- 95o5<%V1LKaGAe4|T}h(c 48@C%'k@ }\[ ȕUa8@?7ϑ`iPG|?'[&N @*|]UE1ڣt23έuk_xħ:B?OFd2=haWSr!&}=iuB-8T"lT'M3 ]ǣɸp%{ Ng;TЖpҋO0p%bǟзILjRϦ"J~;:K+"Rx4٭b&tNrJ&d:qTyB;*"֔x:e)%OOPּLu'~7z}*+UQ'g׸ۮJk:ĕa7EJkl%Un!5ـ8Xe;_|ȀB2LNK6?0N]jbya"Vp~PSwE*M s9M,`bu4T)aDqɖp{?LnEW筀2_"jbfr? AW_R/RRShmų~cX&.M#1~xMch!Sw)_^vG]FkLnGV"t(O]fw|2އ/qobӋ)z>rհTDB-W[uƉGF,*Ih^Nw -}ߡvE q7j̋QJ4A^^W <)[g+GD(W 5#i3X'i>xHKĕ]t (cpouX-4SW.?E<4\: xiIwkBM <"CCrtC< }wu{C̰ y65n9ntkhsǹɖQ_Yyec]%I4u~JU؎2GqOo\a[N4\Rfa^SQLBzO3r]N_*w`O?u+9hVNx^ 9$<%x6PFwތy~>S +am3m@zi܆F9C6'_8$wOkH5!eN*MYf QД qdNpQ졖d8J:kV zNv>; L}'t]Kۊ/KŤۦQAczEXA?F Y'Jt-8M:] ;.c%\fG(|ߛS?}@}ir0k_[Wt./I,;N1[|dny||ZLPt-6a"H>U7_Ϸu/cF <$i@n`ɖ;G=7:*T{?l= \kBtT͋*&kn 3aCiNs<7iPq{8\|\qG-=Wǥ .?vptzaf.r"ss.ww]!F|/ Р@8@1l^= MPh1@zd∽M!]RF0 61rj%4Nt6lW-DG=|Q# c&kc|iϿlij+zzL{UjfUl\ Y |5>/%l>: fWVd1j@֠ih'  9[1.pؓ4)ybpFrGM7@nFZ͏CGGj2ne.e p v>͍]┤֟yVwHZHd=ɎuxuW;SwCOtٰJOQMF}:yLGQZͬ)06 6KN/r-zNsrVvhCC;jct}>PHe_XraP˸y ʺ!F?(3R0anXV Ӹ5,]X\fnoY\rx'wHab*) 36Z%sye+ \lS-zkt+!ԞBv]NzTߒ7Sqo g>Lt5>r&uۘT]J{m) ̊P-F!`F 9?Oz2Hd_Nty nbC^DZ[| 1N?(ЁҳR}%S_B/@3_橚R"@+ktOh[h?KԾ>/1 D Uz%B@(m4u n (77X5mH!׉B4Vz0h&}!~) cҧ"s]hzЍnJ8oa2b<I Bq.9GA C)XV4 NNҙPZ89Xή4F-,ӥ]C8\tKYoߋtguz 7(Rg!"+8/;_zځ==UJ )P}f}ݿmNZ\ nA?vm 3G'1r}ЩZܻbkFS@)lUVbS>U 0jih&%H[ͷTP8HȌ_# v#36D1hPȺIM&4Ƽwѹo2dmԈ1U7NSOn#9+*B') bxʫyT`z#3!|D`ׂ== g-pz%Wy(D =puBӲU|KVOHA*l]5TP NGx 2)qC3`ad'kGLڗp;Gi5rmZǕ({ޗJ Z}?T*HӾxإ:{|ӝ0U)w9?qAv30UQ}_rLFi7yjMzx=q܁Ъi@JS\A la,0'%_84a sLǃIр \iݻ*t\ltv7`T6@H㞇zTh IH2Tɬx*?[L%G Tr2 Ql\u5d%`vwW8'_Bˣ ~yݞM`ܟ֑i7m?N/qS1ј[Hcd]]#ͨ9V7}ge$MD۞Nc;wpfR@ei>rqԫ(C~9<ÆAR/*Rn\ /:QGN*?wAm$7x.,cX2#`hC$=R* <M YuWc g:s !+PK :`*H*X0P-${ 8CQy x"WsJ1 UygQχקvh V*(& VlTEؕ+.2|`oݡbU>' j D jn FI2"AE!a8syw젌Rw=SQBπout KဖR>uT4"b,sYd;v^"`~0G;*@N1ʞx02q)PyCN8j%K{itg6.`iI77p~s9 + ~,SI;6L֧4G5,Ӂ5* sC0\_)N* A.<߮=\we6𰊠?ل6/t;Qn)Lb|8ϣ"cI=ʙ-?2o8 X4*+z6Pa6xY,; \`BKa-q79MVԫT 6;0(ttds8cr&9T<|_l'(G-Z9c9:6&?+\7@x1-Z𮌪rBcn#vZ!N'CWWy(6rQIdl#g+~9V=ڜl׉RlT/~EG+&na}pm6{6s+x̧v.{8*3t׶ Z bEaB waTֈ[Gή> AXh+2j-KI|*0 2CZor6h'e6\eI{)d}o2gū\e܆V$~o>un5Ĩ5=˩(#["P*8lYc~lj4m^,\$eʰ2]Ut]||D .θutJ[,y7_?Ğ]CtJ۲e }gZwj3o yz)?E* &[\żm* W'HusW9~!xD~2~hIJ/B'^F?د,=九~H$9Oԩd*lˎvb:5 SAtLzw98q~an"jfKeYХGpYի K鲼"' i('] ְӗ Ѵwzi;I(v!l\w7Sd Qip)ǘGWHpHۏՁT? ^nyM{Ώ#5iYN4R|Kr @y ɹj5}'SJQpqL͕y0EBE1[Zrd_"GO 6֗sԟܖb_&!byh/V #\R?gHےVIR&x~j)R0+w%zLՌDlo޾D&c/*٩GZ0}ρ}ÿ.o9=~SE-CѸZTU繣4HG9t݄6R2U.I p,8PcU"R''ԫ+|)^`MG>R E}ԃkU%N^ Kp)/B!H!ـӍM]J#[JI(H5C脚_`t_:9UrYv/LscSDjH؃DΜ,q%_cx53O%I~b~^ёu ױyҚ7繽oXuvYqk;XnU2"5aEH}u8K\=Qr$4_v?/_yv<)cڽ4rjMv/RRĮ S^o C?"#N5 7j`w3p?%PH)Dof@ oHLXlwQ_V|߄FT&KH3 ))gʶO|:|apuPK)H;K@ ӁP>_ & B=֎ @uettd9_L|n⛯AAH im]K=l]\$zkcTE d w-ZH2 Hg1kNNScJa|}RgFʵʖaAyxΒyfΪaÄ kO kvV4s-M(j&3*{[ MdVd\edOOjV˭ȓ,eL텴|W?84w!Ȑu<ێ۴Y`X9zqRO3ժޢ˙jJ+`)f^ST۾$+[T[򙵮vrUS"0[O Tq(EE7`%2'ʾoދ Gҍw9-* 07tT.W^[Cɳ8x{!Sg}GnwmwX3G2x;\d#:N~Noɖ^m1ķ{itfɛ'^$(Q3|C0En\bodؾm`##%qyL5,H@ҟ!W4]fWS!U尷/[/欈oaݤ;?^,Mt.cKn܅#1YMzIC'4ݿO~&Oq4 1U(hp7(oА%^X55˫)SƠ3ljǽۤ=~y5JL{|,;W&~o~ksTg܋JK%$b )]@JfަW$ n~oba=c9@>@R`!e)ҮSS;|1akc1NN) < I[ mz.oppUiOoh v'O%[gYtVE~pnh^ytD T#?"Wxb$Sͨ}S^SjVAa$F Ak)xA4C)Y!@2Z"w[TcQGZؾTX>)3mNjtyhMn.+."!!}||TQݚQxrO5| ZQ6n*ˀFݶɿg &\=&;iUGzΎ3v:A 0imwy#YѬdU)7ٜ!ċ\7wwV;n$dRuWFCaq>MO&^E:+ u'^F"Ghf{RQ~5A_bU97}GKUĈtS q^;CvYg5^NKn"} @B12dwwGrM;5]Vst&QlHaX hϐ%c6*Zv/A) %_B /غ= G-|2%pJ 0 :N5)!J!#]xZA^skV;K{̽{<ֹAfq=@Co`+r&Q:^P!Wʒf{쓢)WL [ M{þV\ KT{й˯ʕVfU a]VզkYkf[y)B^ʝiqݗL[uOl }O5!o>j2fxX'Y_Bd=Kh%ROqz#l4'|[|ۣMN oQX jT9B\dK-Gl9%Ce>eBnL9@06IRpQ ›3mfg;g|4rڟ;HP-+Ņ,n*JU&QF!iQ1%Nۈ>~hj:O+@{_=X 5Pi-0(8hWZ|[5FCYhi|J*MTcf_ʖT1?ԩo@=OS$!`b 4 OPt@"ɝq  pWS!+ǬGAC+`>R+ϴ]=-DPGu[]`nJ"ңAo .̵փ7t09<)m&`Ci%!fLJS${0y6v|hS=3CZM`}Qrي;{XǶ)LF$V4boC,fEq=N>?㭛(zqh@HB~_r^ǠeeهlU+"WG:+ſ3Zb Yr_VI^w0h3_u}s@ُF9_x'SQ ![>-̀kȬ 9B"#CF '47Qd@Xټ1Q8ȼ]dB+Ґ~jHY) !RN*нw<@e)4ۣ+H#Y8uOp\X4Ӳpn*Ius:ppD4=W*aH0 څ<]~b6(~zOZ>Rd8#+-ITDOTkc2yٚNj iPN.WsǮAÛFudM]T>cNK>wWK`u,dž,L muNNj > [^%m͟M8 - @qYtIks(VH4% āra pH9EmGP g ɄAG MbP[&ŋpEWn׆ P ^ Uy42ϐ:[¹U(&8d*]xoˌϜl VCbv+' ,yQiwXoR!B~] 8|ޝ8j ):{@")8%.;(1q2'x)(^34)w9]"YRzӴ[y.S1/;K[ZnV~!- lotX;/s"nGg&O'3*Ϩ#`3٭ftotߤlG& t]aNZFN}2WqMaDVy.Q_]+D^:Ї;?Z+H޷L іzqǼQ\%z kk·J b}Y^CZmsTٔ $I)wz.1\ި^芐SR'+pcNs&I! >̦l>)x;I y>@¿%hҹv5f݉s kh]S3S8!IjlZ֑2 Jcx[mxѷyo.o!ief’xv -] U`359HS ߗĵ|Tz 󕔁蟗FM{WcuR|}Zwfڧ\p/stKeG8XX Ps*hSWӛwxV(-Lcr?v;Ļ]RgL^Yu:P+%(Uqg}Ɏn)N欆d0'q!?lZ "O0ЛqL)'sP<]-ygL9mpsĦ:,<ҟvn1$8 oNF͍wr PΞxWFx`qF1'UpwM m(Ak~LӉ ur!!C\hB0u&e,#'V>)ՍܽAKn'= Xā]>r}Htt:]%:RUb뇙bǓs%d)s%I3i}9ǚđzQcSLMRfe.]Qec 9 .T;Gnkv8!Vg:]IJGmbh 7٩htWQw,G8=e/0tDc̅ <DŽi r)TSuW:n@%-e o8! y !W<]6(sGͮ!9^9*t[Bia c=/](Ud5LJ/pYgt4݉ϔי 7C_]J'Thqh<6n<{ן.> G 3T!jR'DWU-i\qƚNh,?(TEr ֣5a{,( >P ,H4j(qls6ύ7q|W&|Ͳ kua _ A%yO:C >岏lR6Aͷ[/P8D@2>:dVDR9 !\Xnd/e&t~`(T*?kJebXi;n- My\A_)B1\ 򥞷){jr}-G?궷\8Wy%v0 5ޫWgYUk2qٵt%zx+ R/5YQ/5T){\GWYw(6ֺv \F*<)6nԙ>tUD"dM-18yGH Ϭ#dMyT@yx">yF 57⟺fk7pg v\`NwawoCcJtfD7U9V3²zX&Jy?V%9ucbݲ1Qݓz%Ac{JzÔ B(W7RlkN'u.VAbt.VAbtꫠL~/~oڝW FÇjq4^Ap18*XN p9!:ICa^aݣp l_ iŠTR⶷2P^Byu4<9Y@* Q~ڬj(?Vcc hP7y(,q)`~4b=Ae9 XI6=9Zũ$Dߩ`3љ>SYΎQYt_9up\ۆ˓zVq9ZH+/WU 94ɠ6ҝ˰ _@.lX;ꐱ$`|tPQ)]WvtOƎïơnKߪb =ƀYVU+Ucl,S֞!( = W. |8Qz޾0/"z'20^CR, iwFԪn6*g~/[3eۓ.)kT=kl[Qs (GW2\6#ɡ}yx6:,nh?&ئ*|2f4ꆈl( qnq8~6g̯\>փMPV6toh8obGލFMo&h5776W6W^)X1+J:gDx_S!z߶]/EfSjO)f-M=|]ɨ/Xنd[Ԁi!WiCc'fCp<&M-Mw؝&K3y1EQ#^(6jNK&iS)pvjOgn6~d R鴚VNʫo+[֮\P[Toc g;(J~Q6wsךNakq܀'&̌fliĴ;bZ֕}B9\H:Z7{L7 a@ ~*I57SVG fr[8r3loJL"}epATW+Jw(B Ƈ@޻w;Vl8 U/wz-\]ƔuE6jdz . _&MB6M59)E};OYEJ&T̞xuIňY*(3N424z=l~Ex.oe>9?!a a4o%V.%V|٦d8i`707M6K;,GtH^@vV2 _D6miqzW⤨A5>A9@*J3g bD,j[}hd4z<0,ڇ7] s'67pnU_&[? 5鰅p2{iWi`5I1#l SI9ڀd$mՕUXשlj4Q*ݞ_=\X.^#KS;ExOeYr (^6VCؤ@Q9'ȵ#i"-è(ݘo0H'sbA~v?tbϟ;uq[X_Iڍ F((ɰ b W{{ӥ4Otn6? rM9qT2%dOXD%e<+S$4 Odh ϙ n;8ѧ(̒RVPfWOY &_U(>IعQG1s-0س â#fXTjvu(I9NP\B4nDs 0bjTC-˽퓵j&,ј*LʇAy'cG=@eqjN0@1 cA43 BXy'*wm C-IIhPE)dA5̫aL+L|>%([;)?SBa,RfMRvOZ.3 SccjV^c41 ty,یb2 +X_<7Z.7r[ :rT{;wE_b$ SYX۔s3:k]um@V&1@ ,PU@󶨔ϢaXޗZ'q3{^)0">EY8&3 Ii(=v`i0(PdΤKS*xV۴t]ShT63)98gJo4-XR~ ^׬F;Q\W8wcO#RD.t'G ] < ړ޴՗ix9ibUh<g j¥Ukj1w ڭ1-ĮV7귦Azmckpy1kٸQ>z}:sc/Zo'W'=іClxT{~/٠{>7FgcW/Q7S^w#r2Mwk|8So0/9;s2twޖq:  Tilb=RՃ8$t@a! 8vŸi$Y(Bj<=cn)7BmU^WTPfӊpKK}˼2 A7dVQ<]A9 =6 ܊M.ݻ{>m ٦%"9yzxӠSj5v0Њh&d\N>mcēs>%{~hjָmvwIo_l7[x4^M4 ׳=g'zͯɖ<̤&55"8\25iOL-}J@WT5sgN@'T701Ʒݨ58P ?+}I8N+Qg&Pg0|mO+j /n YI*TZJ8uCҗs"=LF39YƏ?c5~Z|QϷپ'P2+w^ g#j$*^x.c'q=`a\$S^9`'!2ʣQV$LI} (/sRO2ѥi2.kMvVСiBE"!^"y'n?f/H‡ JVyr'u)Pq tPK{P]$+*hmqI)o㇣3)lx5^YinQ-S.Q.nRwIfmpxC8g(T8B׷&/Zޏ&ߒqwҖ(,^!76n%cS|0iudʾTZ7$;<9!ާ$iC{rHKdտ/~-K%t^Ղ EesXa-6˅a L5G&%ڗ-NnѷvnCjJ9%HPw}څTbG; AٙZB7Ý+d 4 $hi!uly1)s[rS;VU7bGѠ &deY>&?ZpڕJB@Z9;v2ɑ@(ʛ}lr&'-B&rYK7rĬ( J=j6 AOBXC*ea'ݿ|`/#2z/2=`y<~3_Pb%7ͿnnpLf^^l 59b~qQ8J\z[צ(:bnr!9[gX e`N+}e?+}eI?@T?囆/%(Mlŗ?T$ <~`k,O"8ϛw^MNI Nd|Tl9̞ۮE0߳ n ìC{1jl0-"mf#ZzoZYXL{s`nRsrD"uNzNUB~YKV6UJ|O QEd/0qܵ )~ L=!Xgog:uhQbaAבVpjϷlw8{8UB9F`YQwݥVyn ODׂE;uf6t鸩/ ۸cֱx}p/aʌ5y.G eќA2AQylŧ20ԅzM3{-Ba:[X؞dKVoF<V;Lf8@\'-m{ׁ_x{*8+{}f>Lj⪶mX 3=fM0MJ/B?ǭY0 d48t8ty{/&O\0RB|OfcOzO-n+9BzR4z~hc'DIwmitP)0aȇ48(v<^X]Th,\i]Ur]>e.eK!ag_~IX*PnWPjI´J)pR3_l5 foRq煨 ѳGd܈Ăκ%]+s-H?@0> t u/E3P0 i t|On:H2e+E݆L (t (dU2!Py4EIr:*2"keK=V92n ky%gu &Bn$($qyivR}:\ͷBCD;gju&] Z眴iqn(9yJRvYХsǽtI1ʞX OnuQyHbngCQ_Gy=J ME[W`M6nM\[EXYҶS tpViuS`4-؆MBɄיu9g^55ڞj0Gx{ 7s] J@/~!&} /87l߀ X%YO[MYŪ tnDOe!ޝ;N.Qyv;)-B*c¶#HL_aROyR [T)ݽ`7mKuS {H/* QaT\yJk%|VD/*׋e/LTEnk/'ٽ}q-=fDTpGM~ {G=!_op9Nݼ,pҝQ*P_Z?"),>/!HF"  `[V|uVV竳 d.wcWщ>IjqFLU&5(ӫ'WhT 񶳩RQdғƜ%'~}hZ?>](;̾4M7=S"&ݿPE8Rn?jw!ߒfzJ \{H1i<@Y}rհU1ՍSIk 9O{2pzĽΖ %MSwC\྇gdJ W: 4աCtAkBg&kk{&Gģ)Tr?[OAV@'-ɻ3;?'ms$ѽL> 4Uҿqpr1?VV>{0 #w(m i>{{.V-C kngo+%rvB KT>pEK8O3-=G&iZLBxyv sE^o<[AW?ĬS%;-Ίrd2 Yʏ,˛7tN#o=Pk)ig%9E O'S*Sp]E4ɈU;CT+RRU*5s1_‚&J3?3O)dQ3Y\ ~_l38g[ C#O:L3J*۹\<_& PifyY~%~{"^ӜKA%\Q!LSGkA̜T2>wx o]To Dt{_@W[jfۉ+oհbs2UU:6^-&ɝ!1S%*E~x}LV)pzg$gNdeJA_Ql'~!)*3ˬSD͠r]m6SKSAF^K؉[WU[G9,lRX0))` AP)9,GpQN+HBt0GZU<ۯdT֥J Ev~ZDqeE}1M(jtA?C \?)25,ꛆU?;0~ll;|v%j=,d8.UO+3qÐ<,חt$h\O;9B|- 4]K_|VBz RS1p/aMS5Re~Ƣ*)|^QgJYeS@WPmXo|/vLS`ЁS5ܕׇi1 Yњح{Um3,*1xv 揯*iI=$ȌJgIN8*Tn>Je6kUz>SU=*lqe7(hG2òY0Eq2{?{{-uj/y;S-*URx$WΟI~l[!~=^ueTJgpڼ3=w RezVWWd{ Lh6R:Gk?9|2@cګ'ԛL{FCtyoxuކNZ XJq~&cĽt!O8؈il }Y?yɧPcYAtI@_#2f.آ90#(-j~(hXp/KF[5:P{SgЩAbЌ@d~-o=E)1sM9=^>6AIܺWlXA:kxu 0`|>!M\m|dRWo_C@?#ڂA+YK[2Yqd)w*_s̨Q=);y!Rs?pwҊw1șlr{(n-~bСÚ|[cwר7Ɓ' '\^R^(al1?4_v M >XLބ˳=ݖzIeB"Qܟ!21]joL0(=%޿9B]B}{GD<ʞVȐI3&ZW`_JҀG\UJ%K@^d[ui A_pf^z (n(NFT5oMX;~dX9\5mq&~$5 =@v2GVe,rt@ZR"[ejۏ{/ WGOI;  r*AI/:O)9сĨl /Azy]_I/Zm>eyYXpXK:"EQnD2}70=LZ 6C3!M)C|E3<hsH1oi1W;N)-vK.,Av g,Fiا^܃B:F[t&]65e1QYԂxBwA9aE ,yٗfI.f<%E75Uh4}=֦0CW55|YpT=QuU:2˼󪡵W r@1TZEnw:vus[go>w6V2;Q1RzC Цnngv05"G!*_I4TII~? wV[w cvrwoɍ;:C4g5-r5 _Gڬ՗"bBأ},\ƈ뷆`]Z/J1ݾϺ*3ca-F}1q+ѳ!S7na4ʌ'.29 X] Lگssy^<#Mzzd7 xYdʌi6=h~3iwN^Ԋ۽^2ެAt D-$i9z,ip߅{OĕAn:f0c~:U@x`~E4*[٬x,t;9\! d79<ϧS?; -9A}gK⑤%КrQ 0ȰJsp2N-J#  (E}"5 (\橳W@͎bXHH6Zm•sۦ`OW.hCR;|~3zy$!OWX{@AuPhK5wniP& { цK%<3iUB0~M6T8.>t9 gե?7{,]B]ujK̕9/" 08\y']U "\'[:er`h+:P:*{U Q ޝ]džx  R$l Xx Ҧ@e%h Ѯ(3T2''|O7   wwHOAX`S<~{*Ki_/}9?vlXVZofz'VgY B1@Ara&amC`p&B~,Qȱ)!#-!l*88;7*~4hB cknQ!CNxz_QPrvC .|_8Xc% s+0$"29d Gfn%(V7ջllsc/?Fѡt2!)ЏAۊ,`cLb=5Hxj6WQV:m!mbqFǿrz4ӏy#be:y_}zrSjEDU{4T=EjxpTQo*7.[yd<.3B(H:i4 Vړa2$W8яM臰 @Yv9.Hfx)InnQ7| _.on>tY`T/u"Q|G8]TP ^A՛ȼ|g- ԗKT{ID֔Z;njNb Q+ Vo.o.JiUė:*VZMnkոmmokUXz4֣-+=z{٣=z{٣s-ϵ\>:e_.sr1wB. ߆Bv-L |(]kܰ/?X\NI_,60˼L vֿ5OxQ|퀈}&ەc!% -eid oMܗY[_n>7s9T (DEejK9l:p"j}Fg܆н.P^nn|n]Jfy}P!n/uğuWŜ|'*g;[?i3Am Q#Qϻ?ƭ 䰓|@B!9&ߢd`5ؚ-/aRrJj\n . Bo]\AF0uQl#Zi 封NUSu{lڈlhaXDѠrzonxPťhf(W]{"PhKct\ׄ8I!NL.RK\IvAw!7镍e҉Sy77 F&_ᘝqL2i}ꐡy՛:ÚJ&۫ `[ > ,A}gHk%(hf k`AHA\$0K2UXKVYPO#R&@A^sRj-? l)Iccz2@r5GʠRWy=RMn&®Ӻ5yz .VmIɬ>LZ&dtbF,y>d2)<\FvzA:0\~9nłf5f.=LN'dmO/1~p8$ ^߉ -ZlLHnw`mI|So ~R>'@ΈpV&$=wPl> Z.j.x0J}}!tz7`˟SOt:-贄%L:o\\K:YBrsG utYD|l4)nS·~O;Yښ \γ94W/iQwZ=mAkNa(?(j?׏J3+ӐM/s_I+**e*5PB[3Bt 9{$_W)<2#E7ȡ>.ђ SE;펷cEU4ox+VʫDrKW&[f;ׄy 4lF"X& =r3'n34#:5'_ ˥߶=T@rPp{CM/UW\׌p\*קL@BB"xqP'\gRuxZsrF/TxP`bʰ(+k^XٴAHq7 \☓7Z+s30!';1 H < ȯ0F"٧D&O{oB`T~)UCÔXwvU> 7߇εĽ=]IZMOp 34Z2c-s?GP3|yd% w>4liVdXVCm$N:ݩx=9ThѦqh6)LAA0Kї~v[|ʖ]șOL8dQykAl g>mh0e+du: qo2&?oAF?Q;=OqrGk2 &d8QZ#QY =ԚUIOyJ4SPeQ#T-83UZWMx0$J<٤a:WI$XtTI%UiIdֹ8@ UQOF$p:rɬMIUI __PQTpTQX!HQ`E2_α_ ^*eXg\^&lYMs~U BRskSeyOPT=םR^U9]z\p'Btb@C W"űe)ɟk%U4kC|8ಋɄ L@[A&փ ]wOU.5䎦{y:-KbjF: u%ߝ#xZh eI3`,L7CƉD,lyse+gn;2'G[U4d]XU'bj[eQl[0 ueK̲'͓60~pi/~$ŧ(=KH#IqŜ N/7f xZߋз0'p-Ϫ2Mb \3Vq<n*3X5eOH7)iBaWc&C97Ⅸ1qܽcS  wjNt|.B_wTb{6 ;_"q9T# лMm{en=irͧ6_AkO39Bd[?\0zXUrW;(oڜohw3RKo8Xh # so 3;4HeWB,X\ b1LENA X%,xbTAݍ"rz!ϮJ2N4qYԥDYfu&Rn2/->-53p zˠZT[\{Z*_v7gUĘO S?2w5zIFw/" W;D8Q '2+6F *2OW"i$6jo}/O#zܛ"&eZ0 (7wa5[ᐩ7fŹdvY28e4ZO˫79enr3 Nኃl#&VhN@HxGa#Y૧kؐjU+iuDjwyӹ^:2lxxYEy`)PZ0R\{,ӷ -iALt1P0.T$aԗj=-/DJ_V?*OEzK݁~,]Q^6Ax#ed,'1MI+GMVk@}O.)|jJ1YuuQ9ͺt#5uf ^+3Б<; a4xoI[6@%!Z\zǃ)tn7:Bet@uH,œCΩcR>G2\%o[x+ӅR޺IJpi'-2:dNڲajfXC~~ײ-$]Ǝӆn3 B0Hdj .TfB%| 1DƂZn Sh,V%`D~,Yhc*-xا{"]B4 qR1L-_18:e793h]*pOQu|j*=H=LڨRDr׽@=V>*Q@ψZ9="R`/|xjߔ#o`%$} '"+@->ڠ=b}y;?'(Wq曅H<œgԼ+ZL7rl+D 攨9mvDGoZS$E</l2vtCZDpZ-}&/ RxM΢wCNdI`gBYR5a\҉^\Atܞ7`%5 PYpNJYt4Ů&:tX7e$T@P~n],ޣ|w4@P.h)0SvdrOȆt )eR)!8ˍ>k#JR_J~(K;i sؘ3 Ab@Ri$rKZ rr}sT5nŗ|"$/Bċ:m#{QѰDTV(̦S`Vlӻn`7r9;_ehO SH3BGDZBv* pV% ۦcԠ} ?iQ&mSZ gec*JkC)` f}42P?5z|6v\gaՕ:SsNbaJ5a]<`L ]!y[VdkNSRT!0YQtQh*-)UtNO~Rs]rU.KQRGn*SlR8"ʣt˃,]tCx PQ6FK2 FJԗaz(=\#4?PrU0U\  S!jٳcfΧ0jad)H@|̲YAo#`:[|p~!7J@:!-l jlj8ǹ:df0iWaq  v@WM( Wu;?^%Q!'D=0jm\ەboS1HLME0>彅wljUB#7wO?BS9jR:\j>AɰD|Movχ*=egy8uFvaGsl@.8t)$14oB^'+Ȼ'KH]&6A:ºܭroCW՞,j$G"r0D*Q~dS{H{yΰd0Ofr=q>%A$r?DF ?&@[^v.cWwZ.`esj_{U(z0XΞ(%W ˖N9RGsYf?V5W1}X{ZoW^ȜaKh97s#t÷5xPz/5m|0gK9paN9۾{B٤ݭf7(ȝn*fEsm(0SCӶG LSGdx)| 28Iq{s*m$0G!)Ul?.ieE8lJmWN{=XĪGrcѷie3 syxϋNdOX=kH5h[;.Ka՛¡$GmmeS]DEHJ'|_jOQPxM_/`Z=.4M{8"h7%Bex?v5ux-e.7WwE簱 GU2sۛ-,:0mJpRh<9D7T:7\WxT:R{ |I>i!{/ lk0Tkպe)@-OMJ}YR?-rh)c $GwseCɾ6-wg 2B3Z>NwHrﵧaAkQjatzd?pymWHm(l`]iLKCWAW @PMf!D~_Kp={x$W&?-՗aU&aL#r=/iV3OR$JN%;ua2ώx7 t*"9dyrJչS" d .!E >;xrjnSrtø (9M}MېA\9~K{bE ^Yo2-04,W դUܗk~>sA3nTUVK#eyy[Aeٓ:x>Ϸz?(X1'8ǃz!H\Dm)5s 4xdUuk?5=~NC\4}qCJzU:Mis?*i ׶=Jȭ`mMe}m9>^W&j %ZPJ"@{La|xDTjۜ`Cx^W օ%zg2}.96Q$ܛL_BdJknU87%P  RbdhH674` dx=4邑5:8(կ*5p:7|QIot;ZUErݥkĹ5qv,$XȔ|eDwTY*ٜ!ħƃxs}wlL-V DmyìuGA0,AvUl*EQ *εĂrn۱8j'=Ɛ5𺵓;aOДӨGv(jf?VQUQ n֨>Y,4E+RPSͩTQ`<tP==Hq#lS$dkpg۪٭@LJ&ݶl Rձ+ VgOZȊ#i@_ī$<: F!n%_wN(7/F \," V~߂yP R^?ߚp]ٖ%zKQsg~Q4e FWfzJ;q'ymp%Ce( šdt I[·n !aPfOn[7)ȝ>f8~~`!*R;ig둭QQ=LI< lР*v:Ɉ@`(}I6 i{Tf԰z,D)5"72mNWT]HX9)u {ufWn~Jbw5Z2݀t @m|GII!u~1K u9vZ cd: #*z@F$ژ*{>w$#U!m1}92.ˉwXi5Ť+)Y󐒀ˣnECJPg=R>TĨŠ{^gVg7٠U:ZayY.R_B)!*>խr ,}$JTd t]-Bo3t. jnpMKn_Y)C4uE~g>da6 W6ѡ@)`חHLga8IiSmq]nG&뽚Ǯcs\T.5oVRo9oQ3v#GC>LW'mx1wG#Ye֨pI@7KVg{tm҄xD^QG5zY?.jG'弒SŝV'E sX[Jtry寛08ᰲ%2Z%'?b;y}\N |WZ\q@mUB؅px]W {/i F ~!)q`2h;a#K*%B(nf<%^hH$$6SK" Oֆ'y\.WŸ!tl煓rA}:ITXz(00+#YP,BB96[ ̳:V J,C ā&ѕ #ݟ|68#W>5 p,Z1OA,lZy JohuYuIS02T;e5!3H޻e&7w.`SL{Y( K_y[rZhI{{NǙPq7O|漥H <9fGYx^EP{!s !ϋ-[A\"/#S/kF[vVͭt+Yd&]~ak6nl(6i\@+> _K QosTI5Κ|ZCG88O1hyI wLKs.^h6 Y? hV}S()Ӄ gMa0DB $(JD'sj؃bҜN2ЄSo+|# %e }/}Y{"mlkװS\W6|XG 8掴CL77:"R>Nh%NJ ^uqd|Dg餋lǗP ~҇h tko&:NyZJ$ f딒7_8sm^h"xP2|?=ì5騐]'WxZ=Rڝ;c ;2?P >JLфˀ]؄4: mw'#6-{@$ (ꓡE!@RɄe^Ie^ B΍bVh.[F"L.'|Y"*831 _D㆜-9tv1*q.THo !LxNjYs)Кg}owIuƣY˹"p8gy/k/1!ȋw&)+Y23zwX^p9yB+N>,5\f-<7g\W1DG:s ȞluՂWyOyGඝPSŋDfq8ht:r]Nk+NSNi +ހNKUe;< I.j&()kEFGЎLfiL[ܖn/4$n<}pX&Y0C&"/(d$e$@_Sa[EH5 9~53|VU!vYK(iZŻ Mur0.J)l=H8^Jd؝$~AB\"3%.'{^&+?}z}=MF|5Ə3/SLFwZ' Y= Z#V6. K3z֑;l8XP7B"7~ $|_7߄_6q2"PͧVt]IZ֑ c!j?P6sr?NhL q ^`ӊ߬JQSJt R`RQM}?nh v`JLSn{4\$_ Z۾Hzhc )a GdsJKfj"8u\Z^ˊ6ӽ4MUOۧ-}o0Wʾ@5y 7}PIփ3ʸYA{ %ʐ c)3+2R8#Tŀ=ߴ,5n]ZliW*8רض  (')On`[ie,TWGGCd9>|]"!RFv-`֟\9r R5;c ƽa)l"GYq4:+4EBg2 ܸe=%ҒɷT( s07* r >p=ݩş !&]2*ޠ5YōˏVKt YlW Z Y4_\IO'8E:nsQAv8` D|hMhۑLH4bRP7LE !+jV"Mm kX)5Qwz/Qi(*Up*^7Uj&v Cb%app +I'Y֢ fk7Ѡÿ́5&.6N\+ 3,̴%)-i%Nc~EKnp XȀLb<'<&_ 1S!cxl<R6zSC|#x6.i^ Gr}+[yba1fK3e%5ۻ1`8HvC#Zkͪ_}sxҥ@Lc$tZZlU#`Tdnj'Z;'Oo[Sdww}/!;p[pP-ka>-Y2R!KMG 9+y(ErU("Ρ|]iYx0QInWy,*8n 2 yT`;΍n(Y<kB(1v.s„/9iSۚђղC@l{tjjI(jr =E?/yinuiDqNquIkx{Rvc^VRu?=V6ke?k ,1Z<t>.tB%M,..hh9 jGaJ^ϚP+PU,Fj/UW/$UzCg JlU⼯!/!U1Tq`+DץF;Sf Y&J ݹ?BN,iTbHHy3A(勊\i  ѯ]Kjʍ7 R WfS{d؁ީ1Ҩj2Q"琌&iX?hy ]S] },PS6J"t:|쑭&rqCM#˭14_9$~wCMK421Yȵ'מܘP?&y{55Mn 9|Y'=zK@ AEyժ g/bq*a~)c O?xuk&5l@6VJ8s ww9ĨxsuTSwUf;[¹:V[lR KF&#uLa!):H1Grw| ;M UtG]F(Wm/$';i!DAaN a=b G{'0 B;ﴦٗv+kvl}?b6'LaplslJj[Mrߵ#UXjb[;@{/H8eZorD-.ʁn 13 QF9PvA!N^Fc  -[^Npwc]ueJi)߾$#2} >'Y`Hwqt$4CwM ezߙ6i3M'"uJur4}d%FnS(\`$ګ*R%2iI)yhW!tpLhq:v/?W.>sVhƙ?@dX;䕹d#sSV]JY"BSnzacyfue"CAS!a-Oy0ÅݍG"\@ Qgش僼r`}$,"u"Df̮#`N |D)CWe8/ &>Ņ T(IW\ԃFC;TMRB-2.RGz2˒@!w?͘WO=+߸V8ȵ8i-Nqd6f~WaMT"gqqnshvsOI!ίyţhZ<8V0O3gj.ѦnZ ̸zqαrf98;T>0:S3AX0~x DZ~|0{ӖVi|1oXԒSsZhB^u>#:ԁVqj6;gte󣅠ٹ]dٯw1 YfM$FNהѽQk5+yޞro3`dRF.r$|Uv hqб,N^vZtїot\pDxٝ\ؚOB@hHa9oKӾ"}^ ܁逌L٘kXIk"йr@͛EW{uQ = *^Jt]M:w4dC0Q6Lݎ!K^/1ҿҮ2>\*-49c7|:a'.M R฻:6,~qQţQVOa8 r+cp +o.x.*q5f_E)ªSՆ-@xC?w Η2d:L#Ia|f ??J.E2-)u%|;A7x&O3`G`{gi4>p)l(椦r9E):PLpÇd]Ǿ|K=I!WLLr\r&zǕ$,؅E6^Gzuy}^=Ga2*"m ܼrP|[rX:ߠRV!X"yjh"^\S'H4-)ji\#c7k_Xkkc|is_T߈icyBPQ=T3 Tlk7Epps2fz:N@- §2Ɣ،aj(y%"kM7f} dn5+TM߻w]a~&4|'J w{7#c [HߧI=zyzk&{?\beOS6dY{ҳ]S&j(+ދʸ/N'+?6 jށ7r¢!s洧=g >prox1_X`×}= :ohDPXBvs%8R~S gV:B mW3J`*Sʠ[%Fmnpr.upg]Ǝ|ٶs8STlްg,L"q滍;8D<8^h^da\+=C7+DNI=~sQn& 1!VDn[-_ь9o' _=`Z *H>$Bgu)s |p+h)e')"Sw3ANK@K@W,جDRqJ رҹ!7?1lrW#fC ! }X.FzRo'=ӧ-/wr~~\-idT+_/t9CT3 9n"g82v;?4٣9D lAX ,/2g@E]Hf%MgxZC2>8l?xqd\ajr`=8o@PaA]XY6 O^Qcÿ~>[#$t m0R܍,ʤ7Y8P7 7\ffk,<˕:s}T&^M[e\FbQ||\Çx{բ9< *&Pz=ϨoKܠu@$Y4D1J [xߟewYȖG\(%u6?d@+fDF0h|uOۍkc l7.fn{w2@g͸CHBڜ X#Y%F{0x8}PJF4ƽ>Gd 7!; .6v8^?6W]Ws:JczJ?Nkӂl(ED۪8Gm:PNIYgUkҋ_\䧹6(C7u &LѧQ3Q.1jhI6j)rU{{a8}tHiWgaTs`:!(+v -vF-R82oR0TQ%GꡑDV 8ëo/_^ j ZjnĀ6ŖZRCOrŒY+S)|גRR!۽N=tMXX4S%hĠ_ aHˬ޳rNi/ٻ(Z\f h~B? h;O2ZucrUcpXV˒*w57D&\Dm/s*OdC-~=O͐pG>kOQφL8߬‹Utg!@qҍ0Y嬙aoUk!N|6c}$Dm[>eUvXf f`6&[&G[`3"[C 7!g.3ew mg`c72]t|BBkՑeH0-̏w>42/yF6/Ȥ ?QF띳 r^ %L/^ OwNH \*vcOOTJ[F]c/˹qZAf[jzv֥#8FhQ G| RFX8!9b*btUvdT dvo_E*)W4 @CQ)j[8gi+UIP a V Ecie/ØWJ9ϏϜ`* qx|8އ&6«k|RhmӸdw9T_Qru'.R]UD +Qz+, e"8lF֨}m_jDvdzȣf1Hcn j̏Õ0ЂK ,0nC!LnZ[ωgoKUpT;H]?eao =cz kŅ/| k7?^6۵0dʇ8# @;^j²d"߲"@6wCLJx٠ӊ"ߢ0[U]'_qY}귂an}TDH+.x\tf-k? %LH#;jӓccܲ -T87V^ 4 wi9dzksg6 *S>ih :fͼ/ɒxk-hNhǍ mԏ:2}]??o T2cwHY?=zOOӄYXV\/@ͨI͙-gFؓZާhrrk|/ eU^Z`"͎A2 .sEwOrߚ>rKZ0l1]x.,!f« >|0Fe&T\CΏ†d1l(ҝ&'᜝KD'X٨Exj}?r8,/aʗ0%\Q)87oeKr%MÝb. &Qp׾PL$>;|6ķ5vF=W5 #ZcQA;ۋhF8j,$Oj݅ܨ& y6(;,:TċR ÷HO9㐪M(QHl"3#^PMG<:hm:ȨX>>hl(Qsl A44ACk|YX̪m ~SHϗi;* }:C`pךͼ ;:;DRNy3vuɅ58X! ׺< )ZM9k~;pQ1Ql6spE5|՛dvgNU|F}>i,sp OGrW?s13I[# *H087~Eq*̲W7PL~'<^5 _rΎ+:Mcι0!eÎfȷImSN<X纲6{F4 w\;.uzrd/"cx6~na˔dN9αUNCgu =4WrretФ%לv{ ne_9'mkXx&!/xJU8 ƗI쮢|1qT_luT)*1g3rPז&Tιc,W6Fï8Fw9 7j}2SS<,_[&rGv 0N/}do$x4?,+z=Bi(&p Qq1Wkh†e,uNVKxVY>.Šc-v2mqnVKњM~=7r03McᰝD ,i4S= {]*@@Zۛ$a/]8̷w"[Ťyۙ~=>5-t*^f܄ ;k.ܥ6|̰DE³>'7=Mjڮe0"E5tӚ:~e\h1ͨ3@n~KܹA+U~ܙvxvwi4ݑ43ɲ*$9GD;iG=iOe䑕twI%5A;TFG;N=;4V 9fK6.l/^*8kRjh6j7,QOc[ߕg/tZQ 4d2^!mѵXm_UZTu y +r@H[7JTwYIg4X= ^Y^cAzQsM (8*LRnf`b[W`cWh2Gk=j_B"#N0RqF6Bሻ KcANZz#BqAk'/r}q]ns>-ƬW:~WTV93 NO{b3p>狓0*uw{"{[J(LғQx{KaQL13iQ8@H60 E;Լ2sNE64Tmhద']iOxlsh,C z<~.U!?5n{r˂w90 BsHé9` W3&зfXS t^Z0IW6N?Z*f8NQud6qv]:Lf7.|45OxNLZ dp,Z.1ȴf񔏇TO^߷sXZ܀x4 Oj5E%u<! :j2(cK/ q= )qφxYXse)5p$B^q65GAL eO'2m =Z;~VtFYэШ^+30j(` 2;Tk.;io7^Nڈ;PB_`qlhܝg8(^8eA)'ic0@2JiqЅ{Aƴo@!.c{OG, <%rI* eTd ٙ@\>5RQ{cpRt'#5fkZͺ8'Vu5*()S7 ٵ4B\cc*Tn܄wAS^ w]ܒH>ٸof_.]^T2ZQɥj0bi)۩pp0$1Mc4[u*rl [9 mgںX<4{v@b^mVrH =J|1ݺ<[y~ȟ1#?IT|,J~b"$FTj'N YyF5.OYͤMYzRhRғbGbvqR.O٥IQEi-jvrN/G?Zg[7=^yO-y-w?ڈd9j-7,G娝^ڕd>ç7 vG>t47p-.q7Jݏ֒)Y6!Lizgiӏh,%q4 s \E)lMs>l0?S*`^RYpY<&wQ1˕$^  _™lrç(~^8<t!0Qs~kG:?뭽xTQM/\)In47 n]wEv9(-!WD'+JUI񼣹v|DGChd~m`Xo5z/a欬VMq.CXp}vQ$! N`jU`C ķjw6'/&UŌE)IG SbSw=w!- !n`'ϘlE^.OMPh/%Q9mt2.'r5.SPq[ecl4KRECL.MmE=:}oO#/̼cU!"v6j[<?3.Jm/$aEVZ.兗,c.0w[_BlB,?4]ceV|VӨ_5TϮxzoH9\Փ~s5*z֌KUMV/ EYPyc0Zz{ FDOZ׽ C_Y{d' .OO'XAf-rɬ;+ 3:\++tF\PB咷}+Aa`~[Wt ©|7͚zyB E& :q岭q4⦬p|I[ٓZCg|3f1CR>qېZc{.>EַHLӟ޻ 7o-"Р3OuƐJlP~l+xٽ۸IJd!jq:d*ttyK8>۵wݶratw1R%!x8fLR;5ﺃ;'w]s4ƛ.PU_n 'p8"H9 |莧df ` Q!Z; MƢX 0,_TNE=$|ʌF:S BG{d…ᱟY~I CPV$n8inF?3ٮM(`} s0;c! nm(Â%t4)vFy;锏B[:;T9.4doGgCΕƠ*_fǥ}ZjtsVQσz)I /x2 Hy/4 k 4yz[ c?6_<(@SǟM삮س:S܌ v +rnb͝2 #|e+ӑC{YKC92%ֱ &R8e?o0dnOg,,p) w]`8a л,SikL(dAvHv÷"|?!@&]8&qi/a=W-"0rFa[㖫2Qf لGc>P~'p;GYr:,{]7%oyC.Ԉvs8āgg^hJSZG,mZ;YW)k-ݗ[\Ե9 "Hx6e/0S,Q5[d[~? tLc٤D2ݻ*$ͮwJ) % +i3p1_K @l݇#-/_:Udϸ|D~R˝q?O΂񢰤/L  o즇ߘσ^@0\@ .qϨoz]T fqA-7W@Y?N\ωg{HcL)VzgJRc).)?3TU pOO|C#,|e3+ip=N'VLAv A~smQֲ"t)44\]qɈ)z{H9n@J'?B*O5\܉XWqxyy \Sgre;'&3nTf nYćiUq4f7]9{-eS!l $6L߳ηVp>nD=쥊ojpl4Р 2-N+0ǻΠ36Bv;z}5x.8ג :Z4+wq^8 TaVc]ݜ|+\Eg6 hdO~G]JOF#tňWsH67[aa]6 3Oqo8Y|<J9$*,L&.2ֺm.)eg=ޠczL 70aےj[|XtI~1íߝLp51n`Bo"<\,l+ʯ_.F15 G^,>xU;;ó5{z{^Lf>CX4Ge4۫&>Ь=U58/$9Dg9ӂeԡVTCpU|,>e[_5iarn*0Px(N/FlV#Eq%pgP5-FIF֤T_O?+0p_FRF}uxnW&N5!4$&(BM;@.vbyv fqHJUқuѸ1/ Ιbc5\J%LYәEmǶ;^ſd94WRO;`64m>M#û=ڪF&WBP^0}XZgJ] J~[/|Ul/_`9̘.Q`]D/8|U&C `q>mm zv)\ /8;ukp"r(*lM I]W#o1% UC_ r Yq 16.S 1:veKFܒqϴ1qVxYb ?%77FuEBs; i#U5]7A6ĸL؋"jp;xH0 `δwk~Q3;c}~òxKRT\#ѭWO[nuE(8|6K0mJ9F'7;Li`;?& ]MWWԫ#EwB0H.bxOGi@ӞwPqx9vy^yU`cmD#jb4v؄552dP ƈuv]%a2+V- ϯbՆV t\/e> EoiSJЮ}d%58# "9Rr[J*!#-~]m]E-|# ( 4\ W!њ-dMh{7L?oZ7Ta4NJXQޞ;zF7C%6Ҽ>Y!2.z+bķ|oN e }raWANK? /X+b찶_j_~+ѯ*:I~'J[D$0/%+>^|Xu52+(ѳXtʠeg]Fq4]mnn1 M~hԪo)lt 1$x=Dz'V NOs5>'o2 0.=7bbe O{rы Bz<|J uy_R09mW<2?{oF{:^&.(H:p("4#Fh I TWC*FDN PTU!4iPw>IX680>!qabLԩA\Up ˂|E%, !MW#7\??܃8/YcĊ@&Ԧܞrب2MF1o7a] tЙZ*zQ/pdY5ƃA\D0|&+ #\HB7faIŸx<TүKTp0账HVMԠSCٺBZL_RG)] nW*J';CvT&c4ٕdߺBvxO6ؤSjFugV1wV, )?2+3'Q1 F13p.(P%Ka[AZpW8/})R]zP)Lǫ41؄hpF{]꛻=;uY쎼<7R]$j?Prɤk헰 F9PS'&J5 {={,t;l̵{73wSdEYDqq gS6"8 bMu]R@*a{=H~NMfWB#l~\\9ۓ8KE @7UPs, nx|2f7gȂqg^IHsқ_h>6z$ T1%32l1LIaS(!R.F ,өf DSF;4JT%޾cڐuB 0?P9zQ0=%&5#Y&ؙtl:;7ǭ`>?*C޶q^ڳnQoqqv"Ej.V atBxCWF2Q0˳%EDeG!kW0u= V9_Ez:%X?Lw>n&قV.nMlFәT c8ný"lqDZ_-Y 1g0)]Y- ;]@ S :bx{{Y.yq;! ג=OAv߭%t5µxt(5S=}Q|äf{=8˒S;vux:8~pѲPq 5[2mM> fFW "zKF+L |o [/2 !J8n1B,z\PȗOݛY+b4 AGӴgた!`3'9N D67j7;E5j}R/aPқ\J~V1PvnR\ ǵ Er .}+(ZeT}#إ~2N7Mtx j]CݏTT2ۏ?Cc-AgYJX"uxn%,~]/ W}1X9KO)qX@hd[U&Ij$6EH+vSTwj]怭DH}*tQ`8umN +//Th"S@pJuOAoaB{;V*W% mjЍ:(LrZS*Cy`n9v)fEp iu?o^1,@kjΚα~Η_LE.v4b+n&\^^Znń-B 6lBnx/NLQM;kݬ<%^})Q.RExWB2Ӣx,)4&AXV_p,v/biT$"x5=8Pje9 rs]aWMc\"BNԫ #n̰LmlB!zU{x[<큵YIrj;JeIϛy]u4 ( OV)2j] 6ohȼSJN*K'k z8&t0KHe 5p&"co;7 998}U er~W\!SACѫ ̢/668E,+:"-B1\Mwwi7^Nq2*:ѫ})v$W ٮ.i4BA WY7C,rBuf#_u] Cw"\U$eg-xp֚iwɫ ~YjI5K)6 p65W )W{DؽF΃s~fHk:d ANo8nGp nLھ+&eO$qx_JF%: xKs`G/×&3\1nzsL%2ЛQmD_S٠QR$wQ}lrGSJN;1,c4Qě=mfXc:pP{8TFO BGI=6$8ѷڬJ132EI &T2ٕ!_7KtB l9x@d^ ץ 'O?-D BL&ޥ 2-[nU\y _O/:„sBu՟j5|*Ĕ؟א`{/ aM{lT;VLgW XQ\΅d JS3pw!~V%N+=lwFIWғV Yo% )L[.K0 $nN[x oՆAқθ>+SSJ[ed}Dڳخp>xO|띻'9eXZpbabZUh 0¡܉hDKa>JeZ/UOe!@ [zW fLIt$.@j/mgV Pszٲ4 ^ Y wwt s8&7Oly!6 d^Kd3[5c!UAj(l*3"x uJ@Ŋ$1l=.g@"=o{ʈ8{ A{xe` JzvAȕk*%8͂x)~G{M(I YC+$$t#uGR\nT "0u z<=e\nVE%mvD 84yXvnwoiȲ\‘72d |&y *2w , ^ۢ.;%X^ C.^ADT-RPѸ :8| 3Uj@ɣm5;+0Z"@Q.Si JV˘ۯr UʘfV=tڭ֥JO{DcQa<ĨB)-r%xk 8i=EdsK9`:#h ;YzT6vkC7g4kujLFz*6NU2~1Ԯ! <*elgԦxUգ&ןjS%Pj}IzL[r `S>L$Wp8Kd<Is[b&noLltp ^;׉O#fܩ.ID!.IlbȆȚUQ/R"J7}ٛGDOA8&vqzu[KN) K$!35}t9|׌]]ڿJ&Cׁ8򨙐:! lbW7+G1RR# LID/Lvj{¯W1\4fW;Mdff,zCgQih,Z7piթ ) O)3pk ka RA[Xȏ \K= hU T^qA& 3M G6rbfVϹ+9]9ebj8FOYigGq24b!:+C~6TߋY}~|P^۾{S\F stegUk\;GYW>x8{/[V36ɼ*fY\nlI'X$!khBF XhXaH%Zx#z6:3 ȡqlC8rOtm:6vs)a72}k<\s<+ƜDc̻뱑‚4)qOf04[ S.Ɍ?07>Kv.D܎q3@c($뒌/g:=K2bYxY-3SPe I`͠WlRp3\$(I6.B5+ڍ2Sp&kE|S QM;R ^)|BI}Q&L";eJ7E.QuVN{xP )]G@|)5u`Z*Z/B o6HPz @C|QQeҠ\0Z h0[x{b0 mR9NKXJqrjg6cxv-9Y1&E+4#Dox' xmJ(U6j9+I Ÿg \J@?*$Kf@NN+E8Cx8햇dU'm}M^%q+{\3`,:gyf6jfx»n)P./4E*L&ïJtcLJ2]&:NIuO,n`/KOYg䩰9f+panu2K^`w\ 2IlY;k>Yvl6{ɸ' pPPRTDW4uVLeX19S.5Xبc` ӗ2:ş59* $9m^BğT-Y]:˛vO Uɇk:_܆/{7P7Bz<ZN#p\?,:n=-7PJ11Qf..,[tgØLe3?D k̲R[UǕ(((3Z4ʋiuQcQd:+(]ӖanDDYJW&Kd$Ѹ =&-1Iͭ!,].(dܜ63,UQ=N׾T\6c6NݔZ%@^*=7t]2%3^i|jCŹ:V`*9yKj>Ӽ4 ޴zL@T]Uvf*w:ftx2JDDžmw|`ǝ*=9'hY'kt:I`՜I㮘D̖)8r[` #Rrkr`߯ҩ) Luwu8x˄ՂX T/*:oChC4!~y7dTxžtph 5 cFDIx̀Sph_otx;{>!:R*B+]d_jRje`-ߦ>xAe@V ͂^qܓʏg)`%'wH.*U`ޮ8"P,ؒNE3,qյ<yqtAaƕzQ@;<㙃nAtER^gAoJFU ]L- Vބ;ֽ~=('Pew|z掍 eT{aڀ-2ZnJ,qÂ>JQZ)2OV9B3pr `7<_U([ ctȁ.h覨nNcI|9(zRӈ(0ib&ZԌpEMKVD6;+.^*jr8xu4NԨ[]KTMRk am#\mDV!k aԐ^YQi!^u)L%" .]JFClmV_<=[T#v{" NBh})VrT]s<>{a,y~^%En<7+:QLd{qvB+ &՛9cI[jpeš:4GPҭT/bpܿaGr5έi?.sd~ueiGwj& mezXoe'Vokv5@,=IRBaP@xaBkm~6P"{@z=Vͨ}[8T 4Y=~4h/Nh1`|8Be+p]+v R_iUիV|AN.bڼ81Owy?+C4/?DOĕRQ!YQ6T'7hǙ3]N Pa)tOra8[ YmN^sZgxsQ:5.H=Cm`d qm5-P8m5wV4{:#tiW^f2Q6q# e|dJId!2]Z|̸!/}M-16Eu79SRcDžm}z Wp|A閎E=m/3*BE*0*IT)hA. V]vzҩIasC~o@/I'x;y5L8zQOs}w/0'?\MQH+@k'<:0vzux KUzte3C}^jX4@|&l~x_ bཕikEɠ;y9|HrX?iqΟazc~-iD{>jraxRS~ȸN.9,w ~_,@Sn[]B¤gНD!&_V&aXTg؟OPm <ړt, {4 W 0UrGj\UL狗dVr.N߬Y:`2{4d--To.F@V̒RΫhKTu To4e*] KB6 ؅1ܷs;Vw*Tt)í{S_eDdEoG{#Dܟ ;0v4NcjO֮_wu Ev^m}cWٜkHA=8۠҅Ǩɔ:e˯<{^^ n47 GL }+hHnd9<3flj R)qmx\Q7iŅ^XҳV^?hbNYzͦ%q?IWYDOb~Ɓty gmT8=R~{Lk{6nN)+<{qp2Um6џдZΨ)NPfYyטS*CvbQwAͲ~52Q99owM凷&;iCFw^`r0|[D48j޻4qƐ9BxsxSn,qH}cS%ir hчW4$+d:ՕVM9q7Ƚy w2ga5zMFo4C:%o16 JZ A 1;@W 30CsܥDodX:|{MO梦Mf>+fFcyxY?Xap,/!{BrbTna.(g` baWZV,uq,u2+mf-9H*$ gHѥRTfLV c~+*Ԫ}6=I^0P*Xh):UA _ "~udz&\S TGPO}Tk |AjU}EI8~A ێ5|AafB~KCfO?1'*uIlHc*U|a fh׻ :^c4"5gӀFvȞrz5)g]p%eXC dLYac(ࣜV'^7t24آBB@̰=Ն'P㫉lFGd}f1qu2u @qIڢ[D'D~ >D蘨ml=% =h㬊sݗl@TGbBGF#V S߬az!v.?2eMҴ ⡸!N,1{$[jԸNc8cp 0Qs  *aͫ cAm_Sev] wʰp#?h񫐍^W r@Ȭ4l?V2w[_ٹxA/^"@>B&&VXZr {8AMNOo/{@ |\(TE=p6=;6:a԰Au,Of\czٸCt/̂fY[S;kcgvtNC| =/ksmchz 8Sd\fo#tJajхeNmgU^N(i 6-Fdg4wړ}jq!c Q+٥ƷO)Dݎ;.#{X\kEohبðTܕ-HQZ13g.FpbPa12;W;u|p2i=_nOgy^ [a+`}csB~@ٲGb~ъ>GT> 1ڪC T4沠BZFlZIGU|e,ikW`u(9H^3:!gMc WQ#?|Wzg7tcsz =`$)E-ct}賽rFy=bH(7A%8"Pvۆye Xާ>4TZ_];PI{ V )}se ,B(/pq!\= v8"Y3bc&pn$qw^7q(N|<-~~60B+aYC:>vN)e,̗=o84+"g'l8Nd7&S%e9Zh'7RY,ؓE81%eqÈO^O`~H8^?] ɱO,u„A s95g|}Աjz7Z_՟~?D(Y͇If)}v:9uWYW,:nu moKN'TݸAUDN DUe3q#ɸJ*/N]ns~zdG1M~nL;tVLnնJˮ1IToY`@wk/=H zZm 9f DNWaE0ʂ3h^uj[k;Ǎ!B47ቓϟut +.~ isȻkf[gEFg^\9s~iNad 9V "#c,W>˿ "= p-93O~[IY)ızr!̧́s,Ǵm-:Geg^P' |mdeAK|]_\%+{1Njj(f8"Gop("{>#)#]/ ߌn3>9ȼv 1B 8Œ 0&Smk8gIPԪw4Cd0Go0-Ws:d`RMT5Ѵ#Y:$łV:x/K9G٤j+毗Xt% Pt~0* C4}5:nc xx[`T߰ޟ[ucNixu &D!Ü2xWE{ԞX`dwzh.LQWvWǝd8:!0_Qg0ڝHu̅N`t}wa u"C~&je6U ׃<'*7VK94$˒pj<B¯Ჯpa|lZ\~oFRvt[@P!g$e[!X1Yь^/BpJ$0]Atޕ<2ă]$i^}>Y?=p|<=vjFA"zhqpd[6 ̎²̠hiGvtS+GvLYqz- D"'d9ZJrSku,ksHuD0INƖo<+KNvѻOz旗g aƨ';iõ }p2p w\NX|piw}ߕ.hؠ|H0m[Xx0+ME*vX<[x:;O`/|*=M L»3);'d_;v;_kq:x ^ ϡW7= ~Y O:vqc ]0ࡿ'S%v\ TV?lR@kd֣Dt?"w7q4z͚w`+"݁l в佗uØ0_)r(  o>bz0p:m4 b< ($ UN2SDœg:US>~Q2bOâI A?+_HS$$M2”v^E2: ݮ ,r&`M,:s&%qIGE2Jq[U=ZI_5䯱 1L}Ӷ]n@7>DH4wj[rk~5 haP׭ܒG09 "[N*f޻' 2Tgz[tߋ*Cm |j*$~j4 6 (Zk/ L_4`Xx2q`}E;5t8.V֮>'I3e2{NAGSiCLRO5yIbHXf7o)忒 =>S:q1?zc|s mzo(ojlIF>ig汒kwݛ%ugUK;{&u>ƔI< gckLƶF4=if'jptVW Ӗtru]ܼ<αazm Rl%^נ YQ<#gk\AsjQTVa?hDXg2~&E*a4R7RtCu~rc&(rU l9- /8eo3጖YǸgcyD cKa\يg `y͎2 خ'48@ps_>"zHalV?Ԯ), ֹL"»'D+/{8w\W`Dﱅ!P~z<;gwƪ۝]~4 Ps&!3S{R޿Gv8ܺ&^xnuU$!6,b'ooOx5~ 6ݏf'ske8cK^|F7R~{'3Ft  (.  \Yߋ1mwaEg(4+{%fLCl͙lvWs>[45L>aj!ZںRh61yw,26598odжC6 }Y mF.(W.ORԘ:L0}=Zw'doSs*N1xWǰ9t\1Y͚f(eqĬ|.=?rvN<^7`i||lH>\LԳ ; 띔!gXӮ{3WCw2aqd; ()Ds_^ x Re|4O]Fe†5O,bspEX*q\F@٬rY6Ȳ$%Tr|&UKm[kX{7);|`WAdRSG*ؠ`=H]'|ntl:l 1 ri2|B8ت/:r=14Z#O~(X !İv'*Ę>n*fw (\U6 HB-9>Jҕ HLeŒ/?ª*6I줢_y*/. /jУTyg|`KBfINZxvC5unt&'*[ k7,a:Q`7gkY6@S [Tx0Y4*/ofx{0螚/cNo]~h %xeG״Mof&r9fLٳr nbSY5k8(puu}RPAL* q6_┃SU:̝W4Hw9*Mx z?ť"7Be;;VCٯݞ Dm&mX7s=_*mz:f/ }oSg + ;$ mUv!0svA=VJvK{AG(W 9qfqΫ##^ !aڀ[He蹀hZ20j9<폰[ӱ̬t4ҽڝo)5ii8 )- @w-}'(1% hhDWi_5 d)SUȗ4<*s}Z*m܉ʃĆ9>-O acyT8_CWf&%xdDQ2T#v3/ i+F9,Q6O(ϸ2|$WeG㦩q8FG8nD*%ɠ =~|+ް{SkD%LW^JƝLܪ& C[._1 ¢d=Y>lZ7iS+(nL&l34IQ&A &o8DѸ;w#`k8Fw]h8Mvi4h֮zx/{s g`P`j205-k92W3G7y%:UfNave \;y+V$a5Ľ>!J׵ g6_|u^5.-6e;ptśzk'>Y{ *$T&@S)(ҷNώ M:8` I]OB`Gցq=\[PNߝ5RGPI狓aV`3~ӃPʠv*l 3}iowap1.!kܩڣ2n`-%m 'Z_UG5ޚՀ0c-eқ64Bp[\,BT>pF)^Cd]J m%@'z#Kcpa:ð~zxe :#^{ :\;#jv"__'h'PY(m #TXH s?_R:NƜtWƜ2F $8R-Fv@dVfYfMBpNt65C 2$,g<.ys}XTBK_jQPgV 4'+4"UCL0pĄ: \=lNfeW) jQ1iՑoSV$4\`^~)v^bqޛ6C;jJ'Wojå6qB8TVk?@oK kQ|9ޟ{@yhfqڥwF Ȉy _\ϙB6QSval]HǶ"NW|t;X%P:tL"`V(οK;c;^a5{1Ցj.xE *20na \Z% ө}bOuK 6l7ֻ|wP9CY\P:ļK%Pc:M}Xڗ7e,>_6 }M/1:>O܄d;a8>(E:Ribt5 1e߽CQ5\U׊edدޑ)$uWVqK7E\].ϊMW}y}΃ta0v+~yw^<=72+;BwJ9kO=`\6ɂu)gM.s?՗4>va6:%2b}F1CDhoT;f]LqyܸsE:<(԰ zv j(9T2èla,2 Xzu ԤdThƽ=Hh_~D„``rg"64B޺5gֽڒ]?UZ.Sܕnↁ͍G@$SE a Ua dos,pߪO ._JQMBPoI {7sW-!&8`P8jWQbX/KUI D&Pf֧֥b)ײAl,}"5,${-s=D4-5`ДFQ:*G?ȆdRaš~@j[eq [rVnRXE3*] ")8MR^͛mb1V8gmWs `~K+J 9|fm G )aF#2MR"QLLV WׄѺ(ėA׳x ~+R"wus{ןA^ޖ0`ub -7>)ʑ,e1(7nA(M)8<՛z:oUMlY?ׂ vFׄa pAjj6`D*ɷQ3^ TsźMi2ʳ@Fp܆-hwDm&;-֫eK;Zط3?Ͱ4'tB#tVN-t)iBX94V4A"> h{=mߌ_h۽3@ppq 7÷_^olzLg,,ȖA<1jocdԱqN4aPc}KiUXZM-YjNEmO.'Uǎb zX]J>ˠμ黅0\c;N>O.\+ %%y$Tn;C&~ 6 xc];vbeO;QrM)),U}[,|6 ^s΃BkIʋ c&*J*`aO>kfMrE m υU/ke{T=ǵO}*hXE %d)Q$귴Nl$40(nbH%ZXEk_?Q=jZن{Ax|>U®`ha\P"콾 V*|/b~I'jf%*Ace'T@ ~u%7^]jJf$r/ Tu c77Las:!dױ W r>ϫ[.N@,IL'5 Cr.ܿ^7.NC-QbSV7T`l귨 QH-y ~C|KkW+BV?|gO}X[p^*:5]-^Sơx ֋|I }QdrU⑼:~'.KxUl,?@I1)TE 2 f9xN N 9-_t8+IjD<³LԵ mM/V|w^.9?b :"mj?7mSNj5XYLDȠʩcHJ!g$ 3e*wb}-ת )F.˳>%[ !UY#s/>dٴ9uuBx |_W` u'oe`)S_TETD\)+t%:3,+ôQeTdZ{T*d/ 2G⪗ᮮexyu(͇-O=β?S#DfH@7 ԶZ֐WbJk(ںZ.Y=BeWIz[gEt ?E32q`oj H:-FPPY^^;Se5mt9TrW()cFLrTO;h5q1Oʰ` Fim6YBx:J5^݌LWO{Oݵ +/dp Ne+xױ3xP5uH}ؚ:8KA{Oa d2t6)ZMt N{WUO X'KU,q!-ȓ`dHT )Ш9hK3ϕT=r OE7i=jƐ+  *5$VSϚ BZU(MR+FH7<9ҟͯ$}g c9 ~n^34>9f %'4f T ==4Zp("[o8i/V ǓpNmT[ŀ#LFx3+<=&c8U!zx=y σ|vbÆ(Rt>8T`$BD}r='&pQrUVVg6k.]]̩5СD ڥKK8&'ydRS^a p8`bH#m'OC!욿(t3ă֭Kӫ~ םe#.}O0U YWeX3zwXʆ(:*8-~gޱxV7(:@z,Ԯ.a/`xUUf8?o]VCp¥*Ibbv0$]OY+@Vg.Ozq/BFvzwRȮsN|WP@ r|W6-Q.] H)-pT썬1.\SMb%JCpѵ{ 'p<]U##'aC6VWTӰjBTRB78D-xO6ŝbH,A >F]CA2.[W)V0FuU*Hdηi& Ol"F5v)ՏPW<6DL:t׀;hU'95Ǵ-$C2Kd\$@㕛\w\(;B0_atK_b.y=nӀ+~aeȄ AO&Ag .r:6$ oo'%1}]WxUa Q mOqbI&Ak%V@d /&:>|C!ZGA_>zW]N6%Ht(D69:>.kTN!}:qK ^T)j0;N F[g@?I`vZ V2W)튵v b^3$LnGKqj.wζ쎋W89u-z ̷3!7*KwrqsgCCv؜)i,W* HMvGn /up`>?44RN͏)qSa[S5Zf^d{,:2HB!+L%Izu6v7EPI eψ-XƝSF  ,V@dMS"7OTUh :LKZiQYC/f8*۵a*5MW W-aa<<렳D<dq:2aY/~Qɥ{RW"^6i cBGjd`u֋OՐUj/4&)?\JO1`RbRbRbR;jD4UU6p&k`Ys* g49 wPVnvF e2+5ҟ|Z"DZhWʔޚq9M}uz9ATa"GGd ;[BߙOl@~8~34?{4ݶr4; ~duwb\9R=*H L Lv2 Z$Q$Q ˯,=:P!iA7U1pCY)p9w}EוwX* z.Ed0Y@Lqq_gsj5?hǛWA:CI=BLucÕ2Trέ9H4`K P_2Lr)4NQ'=J;->tE9+m6T@xW^?s|k2AqC=BbcoаMPFBBŧR7tvfvwWZeHSvFzjx2h}PF| |9%źT+D]I7l:V]{tۚI]"ŕSt:u CŵSq5Izmg.`xNg4ky[oeZGa%\DV:kƓښZMZw٤>u" 2x寒}'Pk8-ӣFۥ`*:mURiq"`(v!`tLغ?Eu*ӢQ~*lp7FDG^ \Ƨ\^["Cz^D\<X}R%w%>zm _33F70ѦHAQ=CK!~W\gTpxQ9Vi7 ߳ȭL<>|3. p9qv7 2M88-kf6.NK{IxXUcLyȰW՘JU ip=L`[#;?ɆRUʀq=RUZrײ)\\|kBtWJV0l&2;5 i]~uEf:E0*ԭ%ײɨםVЭ/=8'>E%F__l( }Ƞqq~uL2 6 yG@* W1 W\jf/b:tqS$FiR"f`ܦEJzLq=$;p'PQow50_Dqs L=\G Oy.ŊJ~ptlZu泃-%xn!_.V!zucn4S!@d0C$9aGL{MA Gta/E0/c;K=Tƭ)nN"\g"׌>|矬lh.O o8>#t<(۫%gi"߼iPuvۚ3('kw4.H$tWKᯬw Cmrۅ 51wTҫa,65i>7i·_në2Ǭ;[/ H@oHYo80@Y7MT_ƕۻHꚚzeMՈL dKOzp 4_͛N!MUT5PQKOCvz!m ]B`mz]CƘzQ65O#U4#bv8_ac*}f.6TA=|uCd"JEHF A="i$5T/a@{ZD+2](ĞFSD5r/iC4"FO#*2IW`eB,vc/Qj Pvq)kȄ٥u:2AZYM f-Я{;q&6*{A?-]田72A4CJȲR:fSɔMP4Uh3v2S| Nxʜ>}%eE4!a])$AJ^zۢt@G*)oue` {_=!fڶv*Z~%?JUL}5{n6IZnvOsc}t6$݊3iǽO@d֫(4٭ \Y)N3TJ ʘU7IV:h/ՏH|DDNҸ0DuBBE!O/ y_g4&  ;ŀkG#ᚽV5(32@u~8RV)^ѝ)UAr(ۇDoTWd?z),SQ*[iMQqRd4U]IY$պ: 8>=#wE= [/5cAr:$.l!`:NFU(J:<ER`@'iN#eD/AQ+RIe֣] }](?qv>p`dm[p;zTZps4{*i1\D@͠;)5<&U_P%+QHTWHUI 8>WxLj>uʷy-&E,=9^d.gt~>8?j/.5- O"rqנ]WRI`֓-W0 %L=џv`<N5ReFL6xxhՐVYBu1篫Ojs`ϧa0a_ ϧ?˘ArᩂG=4A;)ZJkk͇nO7'9Hʔд3! 1ts}(M|9g!Ypu9_}}s$0fc|bvo:LK%W >Kya VnRyهojPW>\'PI[n՗8קp5a Uo*V2x[ xלuU?kx&Opjh:Mf2W.tgbPJ!v0q,i\@998^5n)SBBW (};%bpwi5YkAL 7M -f:7A9 eUӋ#6[, W#\*҂6͊>Wqd/H koˬ3(ܙ*DdeRIГuMY%:NyS eQ:utdzH9/x"~/0Lt~-]H._:Y3IBݳw/ĸHEA]A4eo9lg>d#cUWTVu~߿'JToZiO~2]͸T_"37[8bKjKJu[Rz27 VSc`Z#0ᾂakS4oGq^dGS~$ijB{ cC01L#GF~/ jH`ו`\V9)%z S49Laˡ1 _PCpx6_n5K{ Ay@1 گvӜO/T` CKHKNCU>L ^$#l:eVe oji++Hzmb#ݏ/$hI4N o>lrZ*$;B3 Xx2W3JS*,LBНLy/>Qr(=- =9j )96/6oav8n sX?~1m0V'LgoZPnQI[Qtym EE3ٝQwl],hzqʰ^, h7"S=[RWf27޴N$ŞRIKK?7YZG•ËJxU6ߏҚA|ku:ERloK8[c=pc  gޑʤuD#A͚`DYۏE\|]j[k/QY*W ll/a "!MmEBI9VwdH%C Z-W2jRʌ_uDIR;W&S)WIjg3V:^'KtxޯX)p{ ]1S!(pHPͻZ’!㶤aA'\fbcTxSJ!0K.kg}RA@0rb$hfC |܁LVEgכ+2U4ka%@M~o%Me|lv^S+P^ЛM `dWu3ߩ Ԣzѭ20mu9r< y+(;o o?[лxxT|(ia1QLcmzׁ'/E( o=yuc]znM}AZwr-\O/K?Bonʰ &G>YBW) & M e18Afv;;a@|1)GnR!ϛrEis_0{dk6 WvV,.hBiQ=NfQMFɾVjw*EO$1dEk0kM=c\,FDOpEcKk}'H.AwD ">I詁SWO`VZG9~=&OܐWͿ-(pӧhaW#TOLro]XkW,; bqǯH i;NR3*y7H7ӛ9+)-1~z]qi1}$e{{N!{=͉F. 492Wz?xk2ɤ'Ln?iDCZY_ / DQz{w /yq$uW@N&%*|3&0j5pwQF˝J'?"Pu(?`Y`cp#p:,6hD1 =S¼q@#B<(GN o&ӠJ&Y'*c@(ɂ/Mz7*Aw) X "'S4hAW a^hJuGAD8Hts^ԍV$=|1]Gk4n4씞av1&}JR JKZ+ T/ S G%PQ\ ʳF%_PA'!F9s fol02*A `M Đ$ ʲ.O~D9"V2a1E\>Uup&اwч)/O|0 ү 4Bx0s㲬1 bU!1pW4RpvƱ1Nj mvem=r9 mbT&Fƽ %߅É_ lۃ 6[SiR} gq n][".$2,@H ۨZJb:)²Kzֺ ;G8 2:?f\ *@T0 Xo65 ƭ=?{|pG;+=%X`~r2?{:Vay-D EHEVySIMp |5?.^B8^V#v5gz\=x`]^ǰ#].YB8܉pMdr7{=(Y; 0T4/]-smITQ1Lrr@,pPsxÑ=BEnþLp0&6yqwqsaz̰K`Owŕk+3bQ2Ň_0"ɑtRlG jyKVnAߪ 2P\V%X.FzrW\ Ւng‘Ybn8 K2ITcQ+bK;9(4&Lm6{VϜJu0u@:;PvwEmF:wNb{n7K<GFH;>qiPs_~.P )?4t}y@ xప4=͏H5'1J2ԷB~*%#P)e*+90?d(,a'h+UdBH{?QI|~RI"5%4e]TN'`n}D^dj#{#cL6xuY\804tzdv6]67Nֳ1 :w" O|n9.1/)xEf[  ᔭ8\[kI[w֓cay;?fn b5D pAk$߰Z/GZiX SsP!Op}XHkxM)i ;H &B `."%S+sAcը6j mwsƅnS[2sDž.v.'H{ "Āi\c|!F54!w_$mW #.c]uYI|U 1*6{Y?De+$@iޝLmBA;2e V2|s>-PjD<.TN:@:pYn3| rFKt?([03U8.-ACc}x<]INTmQ^X2x.1He/.i_ wLU5$x\GV<vh0 E,2 E2?|~eo6D 'u3I|۱=^z_P%q,.&)/:UTNO, BDTM7  rɥX b^F#颶"s"e?ӿG|UOvvش4ɮ;i3w޾W(XmK{} ?rjߟhdsu*x9m/`4o]_.&{^Gwpz?}5zqvΚ~AVRDHaYq-Z|?#\a 1}GKyZCuV8Oĥq8 1E=', !n#W Ͳs]+d@/.Gط f"8ڨ夸is^q' Jmft57B)!RHǢnFjfOj>ŏ*[a<,JnX/C`GGW^nJ0ERI6Fq4z-;)G\ _wHCLs>yR5r4J n,45{7B-ګq9)\ 0O֎ -hOr [;gl ,wͼZkTygcz{'}/OKo\EVI3r[vEWtv6 ?a/jCpZM>WUOϫ("?#r泗X-ޯW|*k1JM0QC"H6eAF6?ď=*>0"N  VW7- iݾ0pb`HS>ږnvkI ӄ bXoKsVFqYߕzY`} :R<\,ߨ㛫]W0qݗ+ܖ#z)~+)+ƠXoħ06)em5}Opsq__`ʞpZ}d4K'>]n^ᅗ䫇~a]|*ݣ.+$UYˣ}WCO A*jӲ\:PM8vM3>&qi.(ځġ͠9zn3OR&xQU;ywlF7pټd/^U zսt @>XB9I܇aWXx(bE_4r (*N'V9:&GHS{w8ĖtaA_v`ZN,Vۺl^k5aRỮݪ7&a6nDKwaZQׯaýYoz%$BՊ&ob_飘xFc {/4vlrr<\p$ƗdeG=rsXٱ{0 QzXqNpU3 G/}̠|v7}%LATWAERv0HqeeaTB(K7f;MWu^%6{Q7S//XNqo-05%.ij|ҕ+/sS :lDyjYrˆ>RVaCs1ŦZ8cmoiiW)AgFV5_rdҹMt‹wgZeCXZd|7p/YU,}tWP˦0AH/{7{m$Z0HWfu?sIcFk&hq{ {u3Kc=U0_=xKLLW,PX]ؙ;56)խ}a>cg(N:ܢ8 CVohQAEY* ?>Oor)\\ ]yPCKXBS ?6MF,9g>lU;)sWreOiwU|(}69DCϓN3o)Hq[B ["_9YH4C8EpU4f$pA{S=|L0 R9 0%cv kВѢg"^GJ/F7ݥL| ^g>;8 Z[U9GcC+;3L4k}dYec_J9m_yq+VP4⌽] ֆ2.K&f=7B1}3 xZDR#}W-rŪRW\6۴Yup*vVU,X9QQpF2W]$CgDŤfV!efDj_1-K1}5fjV/r!3bxWlC#΂v|؍n:a6⤀~vUQ2qFU!{oZR&?VN]Zu2ꚽטBҭ? 3 ^)$j,0Y0Ri bX"*e} c~K) ^ te4jt՚+xQ\\/r{vq~p}fKTR?Sibm++M_~<1[_]0$E h8P 7 BwW_Hѽ`D mRjHÛ t8LMWVcC}ISK|cZΘZy6F\^}=C\6 L7wph_KLRJ^ yI}ÈUqidL L b+}Hh2L2!ߗO lcLO]ќD̅ Te()TϚz<,N 9]!ZQ$'4,,4%CeGݸ(|WӰ֧jF>+~~{'х1ʕiW`C5s8\~ uf7Q1%T-|ن*fq&\u]L劔+SЈ! 5C? 5nï+o$ 4iB! /B!~O^ޜi-z}r&, OWAO/+Wfhby䦈,7A Y |K3~|1?`"k@)J#c_EC(Ǐ9Ƞ ◖sKt9VХD*( o%z<,@g@BI#_޽.IBEwhk(TK/]gk~,hyYH?bz-g^A4zY FH! f-~Ȣ%/]b ic>чؗLJ3/peϾI>9|w5Cf&ϪدTr`Sf0r%+<_b3\Ja ᧀdnY^,Bg4 ̑mm Z/ƺ[#G;Ġks"g7ۊI'?.諁KomM/~C5!BŇs V}߯G t6{{NMf5hY,9ȓ.'ܫ_Úm l4xg׃X0*!l%m9 p;uzμ9_h1¹=Pf!PnGk!aV>Wf"T.Ķ[ '3ݐXy?_S0Q2&9L" .b­htNȌ—v:32W94j*:J!c+v돺| x2 k4Nm쫻=[ Ěܝ?^];?'ȩKZ!,9ɤ(,ގc454^54bUqno1BH؎ %l+_DzL~lMˏ4â9k5uAAt $]Iƙ ?kP\wh,%`#(7YKk|]I4iFUQ' 0jUG 5wOExE*}Lp?L|yG$DHŭ>Kڄ{*.i}Xw֋Uݏ]u bZ k< zFc>nԥ2| * ,'q"|O,K){EԽHTes%lotQG{mQyA;-h.N\;#g :/Žni:M7sfcY`s7#y4錝6FFA۷&i̲DBɑGX ^8^]dvB2;' (yq_q;\U4TtY3ԘkqwF߫?P\;۰RF] 0%fZ~4˪};mpw{"bM`g7EǏnOU y:_m3 텛EKe &{7zJ00gVd]&q'.l奻|Iؗl,9gO6IHG3/!8ȃx~\}crvw'ڄ> 5\Kإ?xnDV162[v,v chIhF^Rve&&AbLpS=10N4'3Jv+ԁEv|񴹇)=^䲵|BW[G֚--d'5R1oxwcx mz(C݌(,+hCїYFœulEޭWS: Sl >c5ڈNzY [{g_YQ$-[·ɚ+iT8L;Q0Y Se 'ʈ/qG`;wԑe :¥NhYg_YRfAoh"~ɶ5 (q.٠1t<ϔ =*(06#gb'O̗$Ewz䟆UKMY 6= Y>7"d߼;fx~ a,@9&*PhYaWPTލp6a,Ao?!zSG8٧$Kl]=]TA|UTuU6Nkq)U!fJֈ@nO 릙Vj1I'h|.q,7x) uXσxՅmu;?3TFj# jz-UvCב4ߔv3YՠaTm6:._ eA;}åk- 쀩ĂڕC9MlN@KkPFP%wgdA߬"lH V0gp%]XEV\DOW38s) gwWGf{ /Qk4j56;:\ZSlO̵&.߯ Q4_QS"Mk(Ÿꋍؠ:MBTk IS]ZUi|(Ys7= L͛ r$O0iƁvtlf@H/K!Vxqs hmS/isW:ˇn΋g珷E߸紟VdߏˣAk~Lʿ0;脘,jwqgXq@S.>X\^;:vAgT_4{(e9}0۳6VFH^9OQ·;|6ŏ4B]\SeTh"9sۛr\`H56ɛ(CVb-u{X 1YWj_  fvvq~}yvtg{]"B(w< B;ŠD]Z,9:P )Yԓ2K?~l,V"Xl>5 QB%NmL;MRcqńupkquUNk z䦇oG@70%ސϲ-Z{mi!9$S[۷GLv3XK"˲mPlPH[50j,HʭVЈ'ro, NY!|n Y%.5.!t-֎>ZH^k0sLzyZlWYhdr8X.H@ $eq4h 86:H:G) iFpO i Bbd49PoLr3Ej6X5X?An=#37a0}a5u1fטÐqet D0tnb`U3s┍zr7Hģ#LEf۩VGp8MŻLIbZlNkUh;X[ܮhCTjCq9}r80rDŽg ϡu5m59ë z/|J;Ŏ 6iWhޛtN]lt^\2Wh/Y?Ԫ}oDr7u8mLHA}km5]GPKBȻmY!@VlpB(e_79 0 <ܟN(s ~Y:Bn &lZA=N. n7[S=u`IfQ&Gc<45/ƒ_ R6d2 \X-饀+ IƖ'&|mȥx;ߍӌj`*vꬊ( ;0f I(<%@b3,! #v4mnw)9LsqeS8OC7ڴz]ٜ2cN_~ş??1g$98'7,Epq"pO?,_O𣢐ⳋ<6@:VM~ VTAhK˖RwqVCgP#pzSo_~k %s,s۽ZF"s:nn~'m\OCOPo&&uؿ.x;#6˿h+0>@x&Ib 唧oӏ#wa2jK.ie景_~x['l=ɽ, nS[^C9&V}Cus85#Ƹ^UPƞoVps<)]81Sfk`T.>NM–VV vC;+a\J^s9dw%?x8 89_Aa5x v0tۊQX5O󫥖wj˞WO]C”Ů9-[ˤCkH{!Qzw‡a]}x1{dj:1Œ##P?yIp ~?,i?ϯ?z vrX,w&_W>6ٷܣr'}(-.oS FKT;s#+Sߋn6~Ŧ8)4~ \Φlu(ES5KsfZ$XDpb|^_ \$021|>Pjxf&p@_U@@ؠ=l\Yi%|gk!wVb[p++ob=gr_1hqK%ȶ,v[ 3?g;._&ʇvo}pO9AP'~bc~FeY*~#|<*׿Ê9Owf>1"@r ej]Ϯ?uBw]ྫBgB]"×u}K ظ++é5mƚorɱY-8%bEOU='!vq"w,cɴ$;4q$e[g8*(Dۋn@DM)e$K953o(;Jj jbKljacf@oUoYȥ8DX$lᤁ| ^ ao Hj3h}0 -C|ljzzx>v@tmN%աN6z]hWcw؝ RM&ϴ9QTi]ElӋXW/bWo ]N& HW^Z Qz,vO%L wIl]TXk@ ;OF|\*.!_JL8kqv)NW74XtآCD%Jt{p  {\T)g &4Loh"@Mwj:7 d$$8:xR`TQXnnؠ  I>uh'q7O}ph 7WlNOY'W2 1F+dF#Iw59%Yjh/CA& 6 T4EP ڹGնj8 Nuɼ&.%+V.y(8u<0q^RM5zf!^PYv $YkR" Enܻ,+6xedX_'U?.\ˊ޵ZQR/Q>[gd;pfftK3=YMX)zm@N(l v*` D(._k-Ij mne s# eu\5M3O7OvseEo34| Kɀ 7R=C^ cm7 h"KyepB ײPr8 P6{3ͬfM {&>H1λ0n#ZY$_? g^s qX~W^^HkmƫA2X_w&o^x)4iiOncVxf@ hBVQ]?%Z%[0U9@ c *BēUc3^ɘ?mbh&q`|Zvog]>kw^v \971͚,c! lxY&5jf?!mlNeȄ1uU)򊅋nO-W0V cwJкC;e{w6]a:=>aiQ(>^9#</l쓂C%y5a>Tm3eF.-BgdYoө]~x7˻_[>r-JX6:ڎDr\c7*7k"$#A*u7mz;jNU5n8dV;^723 w.g 8qg ~N{t+,Q?DȡW=q]?SaDyh!fܼMaj`%z u@I ߐEv߼[w+w"`+{\c/}=;͙bfGO~S1&q%ax%y˝˟X Nr3-F'>%,"JG[FؙA՗E5۲GܫцM)<%?X @ ` oxY.,3}`M,G!38Ըz`'n4҃h>)DK)[QȶB#(6=)dZd%D6'a(ۓ{cqOAlф R&|j^aQUS S2jF3[Z=6EfH7iB]|&~DwOTl><sey1~L!r > xPp]Myx 1556MS /$,QmD/N^_FkPRS]8A8WRGw,W緷]%8>;Sۇ P8C?(7lڀrE/j`N Q:59 -1Ae^`qs$_srΒd-6>U6OU8L3ŚhGco4XJBH ː^yީ5[{D6˅= őksq`١11@Vh+AhoE"o*EMqm%eC?QvK,}#I1rt2؉k=mmї)WdУm>euwcr㚿 ZS&㘸0I{P1:UEHmM+g=6ZB!Ng2x M3O"|Y-)AEZy/t'.е /FxOٯ745_uc ^w#k4BWehH,ds=իn*uw­vT:-~i+ڏj%%%K/}~odž Ǡr &w'暏o IfJMLq$;v4>b6277X`L(+)Fn15`lvp/pY|V"͒~}>v7xhb.8.[$?pcIae^:=)?sySE<2KAl'6C<is]\ܝ}}5"]_Ie 7e Pd9" ͵e@w zOsKq|vHM.)~~_;#zĸi|2`kd>>p 4ٹbu䈖\=-i;^F-jS\z<~&Wj)KJy+ݺKȾږ>_aIv#>_"0Wqtͭ/ O԰J xWpmi{IgFN؍ʈ)꘣WF )% : |fGU䳶~Z:^%VC<|p@2 +_iĥۊ4am_WmWؔmk̓񣙶ӢoqNj^xkU3*>TouV<]p6A6λZxcŰY_+8=r^ a0Nv',Y$^ cҎkPӋdr7ozN톥xoP*Tn V^{=ǟ_++3+l ^Wl@-vO*rg+G~/ 4 :BfQϜc=--QZg|9 Q#&!+|MD 334J=Mm"^4L|`h%s6 k,>?ubK bH uqh}z4@}ga@K/QF(غQ̡}@"I-J(Fޖ`x1ؗ-Vl3VS)1)a>bUa&#m?ڨ(fI{ c`Džrn"O(?zd:jˎ^ Qn.=x"@_7J~Q0NC右<'ʇ.xrn?WOQE|LoL^0B & ~1\'Y?<>&H?Wn.S⏛gA1T< ˽Gdptϻ!rMuvyMz2Bb O$ RVޓLDn7Z-v5=8suE׌ϼvhKi&N &chݻr":=­ D27)KkxvCֿDZ C5zۧG+3h5%(abZt}4Bg1k0ܖWl/z}'aM(dMz~~B 3è#6e($u OK7G( v'vɭ/ >ĀGٗ$Jl%%0u&og; "4G`[ܓ 8OvH͔dϷ{ k熇fx7sАq|XVW!|J5m >Q<:iMt:G.<IKI ^qp]%x)&œ2@1O/^vPX0d&J!{HۙAUNXN(ԒoC\m8Ӄ4As4W e9ϑ%GKllY)'j#I`W\/MBD.Ⓣ@dqoGot .h㆛ɵ3ε} QA%ӻc;gyL?٫p؝] u/P~,uWr4e!y.Ohš)u*Nvs',;nL <0s_Wq|Qp77uްpxe'GQ i}N3Mhmh Qyg&]ݿs/8XXl8p;`0 M$r˞vEj[`2JoaaֻM=n5f?tJdmAGvZS p9,c񷧁+uijwxDhOݙIkz"%!X*fVڲC3$:9eTJPWc taZPvebc>+XQ*GAYl鶖*\5={]@Ьֈc%($*wX{~7c;"+_H;fxW@CڢLAO.~0 ˛s79]_~s _.f#Z9$9w 5ܜOp\#..ϟ+[H[0P!9߂(&qus~&AHREmn~{x͎<}p0:\^#fx3QM {}6a4&ο_f?=ʙ^\ܝAuD8?O>(;\|yFvMn/nQI;Mry06V%dypIݼޯ/I+xƂ&&==dX6IЭ?~B5d0 9Xn ><9&USA~}:+]JXiD|Sqq@BhF[vT5ycg=S"癷r5 Ŋ|lH޹>E|?%P5=COBÔ7@3ɖ)+,F'6y|>yy~W-Dm-ؾrqU|ٟ d0bisQ7JcgbG17q8юօ8iw!hN??x d<*w,c]!V6[EB8MD}P&x2ҬxO}x]9)5G-ײ]Uˏ{=UPw.H1$oҔ*QQ()~s;66{~-qYϹZFWml*!3( BD1G4YVw.6]F%PͧhwQk3Ϛ%Je5W} 2kWw{ymr6#>·EC|<|\XLqKM31sk<=G)M tuns3gsT}g_(<`/<^wn|.G3}=ѷt(ϊ>^F=4T` Gz|ǠG9GkHvE/R~WYW+E`ؒ6nQCAnςQO똜r I;tWDD&398I>`.4UK&S"xRpe>lǤ. kB6k0_|!WoC/u_p|FwV+JDy|eIOm\!g2}oz³XX$݄6֞p+D4?~iࣚ^@X 4]0&i8i(3roGYq_.[6Sd":Oq VYJp$Dp<ڗu# V|< OzS.LjV>W}Uvw;MAAaL?kV 08œ_b-WGդA͂;k2 [6ynMy,<=և6xzɱ[ :|cn>Hڷd,7bw8sV/ƾnQP3ՎvKPBvrmК5>6L= B~eh )ePES\UOGٝ;7oVeBU(Wh1ǻwS jeǎ Zw @=umC#]Vrbwlf]l b>L22]B{;EJ?;kڸCcm?]nM $ IצKWE_ƀ·ݖ+]l  `@4w @ ^f*ҼTdyh>a̡:FB$u⡚6oy.K79<&4CJ8*]=o{'HC&E#aV)}uE?pK""S q/3o#13pFك[k8ieu RUcLFnLΎ 㞓"c,`κҬh[7V]R|K;t?|,8z%^Y/OK=lH0`SJk,ICJVvLa<뮕`3 ;SDTRoX2asld!sA^{(R2-- Ђe5޾qVl$'e2 lh!Ơ3ĺ͓_eN邜+&0|f) y MLRa#xxɂ"?X4CD\+ڙp.~R_RsXo١Hٔ}6 ~&2püܧ8ׇΫ'w脠bxs8|Hh|Y1ɍ^z'yp/4SM: s(xVJ#ې~Dv/BءT8@q3s~<_Il/B(kƗnw{~]0-^78n!hY!8@mtPXvHǀ 3hbw~xh2pq;%dP+$إ<*^0SA1I-O_ThR DBԞjjXgxU1hHmhT-f4!2S5 RzmBrm4)rR2ԭ!gCr2xdі]9 ytt ]ikU$bjRi=QlegMm!A̴g ;﯂n d$ n/7)9c96FmpN<[Ck:"6874GחY OJm *˧8KŇjp&p|4'dNQU IyxF,h4Ct"!\B}m,Z:3spX6u*t^Xhe8Bi|R1\fKFTXQא2~QL-Z_etW=uHR'pf|='0p-yr~/>udI#1C6Cs0lir|n[ÁC lY7&kv@G5gѺ>ґ8Nv)Q5Sd,xUc%XюGoPT1* 9վ^%b0gjYX?BF`|#M0\ fU(q&x!^8+Rl;KTk*j88͑XB!E{͕lkՈ3O>t nax I~^!|q$J~㡖=?x82ΊE^4㙬Y|7hr^Bhh3 e171V L٫s.G2QnXŗS1Y9AMeIl4xh.&uVtz3'gIh={wV3c%̂g&$GrGEGqN>g&ɓɚ'9*/KK%(T|bQs=Pڸ eL1fN %)1q4drz1|^d[fv$jc)DŽnweGK~,xXy 轳w`G6x(ҷRx5I.CqvBv0d)3/x p''BiqtFD>YFGYEt-T S5 3ӀMĔU.S&D>03rgaƟnV#5aG'1<-H}^dOY&$Gӈ Aё{KN5CT87Kf푟K&fe,agܨ]əG>R" -!~i@-9*ʑ2<֬I~&g^fU:ڿ!͓ꔧiBR%fٸ-bzګtBbMv(K1} 65 C?v؈%wD8>7xrp/tO36P!Bnu,h`;퀄30 9lTՖsN6^e~"Qm:R|; 32_!jq=2N: Fˣl$i6HY>; gPCHU`1y 7 PG WZ`^`UsOTԖuS K9ClK>T8L!Յ`|3i1$wk Ѯ"Ľ)@CK>A#spvZos1P^5]Ν()UwSTC26}NM5=t}o)Uemܔ)uy4G:\:Ya[n˒yK}Fqev\V L$9Czڙ<SR5fB|ϪścB} 7F y> H4Yx1'8+YiG}RB^W,KY]b2/$Mm~'L2 =Ϫ`H[@6ĉFrnzs])3Seb2VboB];hOl%_+kƌX={cj{؋K ()̫eP{a{D#ٞw';EѢ̖t!2쾱q%۱`49hk+DX~W l֖ÔӆF5/ s.SgYlE1G;GZLXܲѱ LvlX#eȝg(wזs]#.|S!p!hv ^01=giantG&ٳ5םtRMMh(+ĩW/)8Wj-!1BЎTGdb^ڏj>Q.UmcV/K>9I@DAz?ۀs`12~-up@ITIץšA[+ |$7=W̐l?"-O NWClz˘gG+\>hؼQ&N jte(IL ,:1ԙ+NDur 휦qy2IL@kF St#|]˚/G.H? /an# xM0 v=V)Nӳߧ$#;HK~ ٚMF'_VO[V" zU0 -Ud枡Hh2r2$tIzfnxf>z~,,i2pxh])*ܰj̾/U¡<) rX}:I`|OmӞ4t2ꤵr׵NrkOצߪPS-ovh.[PxR1z~k]f]tKą $w)$2TfiGPGXJx7ߢ>$`Z]]*gL!uWaf_g;( F 7`'7*q9OKזv|wڥXI S@_Pg(g ']!<9+)ӡԏٚ"<@Y-0b5'VJhXy5, aJN il?=ՁTn꺽I0̞,BN f$AQCXNM+,Y\eB3mǒsKofd˹gєMYr]s=OA|~H{ K´֦8F܃qjRH1Cu0!;CPlnWLB$A {(HcXݱ/HhfٽR-afjj::l7OK0}u#4K˔]v oQRٜ@dl<4.6L>kE=z_0Zϻܘ\)K#ĉGrf\> 1Ns/Mɬ`ڒKk $z0Mt(mS4 x8M.f/ 184u^R, Kd'syؽ3pHm*d y~,(VtJ*3!l(פeFNBżePּU-R53]w~iN:yKCFb 9zٖ@B^X֡amx*xz^IǭB|O> 6rxvU+7ocs)k MU컕@'Z ?vSXw!nXtLP[ Q˂<W7- _gu `b_f .XiD ^8z> MlNݠu?@јAFeg:0gfMפ- .ɊX찾<^"1Q[K192ņj6mxS)C ,5O#3a9W=p/j/F;0bj$z8eࣁ1 0OLcn,DV c,4Ι@"j߆AQ=]Bo."F̍Kt>EqNsTESG]2M饝1⚶iɗCgv$V˜trB\LjEB.ԝ(XQqHw~ekʢ_3rN/m1x?Bӗ+CS߂@3Nj?-]uoމMぉEfǦ(BF?:ԢerڹF֩\-:$| *XjA~|?AM? 5PV^è`tҠ+BEulpa=f8MFFCH<6tVJ#o5&Rdr"p'eWBzh"z`*GjQ ?D6Ǥ}u;cuZk㍋>H.a't6-m#KA6 ̯,[b<Kv*ZiV"-:3~UW+ף@1"jAJ'cHDg.qn>A{Z.4ޟ+BUqM+vfA12ץ=20o$ٝ:LvA6;l X!0{5,р c,֟ӂ ھN}"š̖B_ 9 |i3i مΚ[Sկ*!h(״iIz΂(է+!J.mV{bNV.DA{[E0Dv\aQ5(!#Ń@T[}?|m}oDMٯ'De9[9ڜD"CAm֖ffĉ''NB?9esLF`:* (6~ q~ QFۼ̾6mv[1znΪ'F(`bCWrUCԠme7KRNY m4@ŕcYVQ)C8GU}({?tE >OzGlà2 &ºs[jh7CR6n{Ypֻbܾfڲ>sO2Vò;=LHB ,>ȍ1_1N U]OoT6g3GKR ]|Ltv+#%"2+2;ʼJf6Wg2EXn͙~ AW/?!10O(sEnD`!rJ6p; }qBϗhE3]{x'>hXJ$X8akELMѸȑs:&flRk۸@`kC>8FHxչ u`ф¤qB&MK v4H@o#UGyF:XȃTy!hUiPK?J {Z ssgyE)G8l#xPQHCX}6 htS0ɻFc.!g`_<ɫʽ{}XbLj \\˄ cR$X̛F| ïxRɨ6Xw";"KLB18sKBr82ߧVHc<3Ϯ:l6%xO9d 3Rh"ύ̬'`Wj%x4)1󔈝;x؊83hs(|^5E]ɗH;R~DUg6RhC,QOAjlq?}3>7q',J[`17>#3 L1ǽSoPk9"jisr˨1 V}+- x7X86z.4^t8*Pb`rL/OVrEJh&RՎ٥3N,3"q^ʤc *Ă=FPd w29hDH+Žy!& qű_PYT93+̲_YEGA2\孯 9dd*@Cc`z;`Hcf=,%rxΛH/,BKK#ILcL;"S뙶05nr:VN#Q7c-!&E@ܓ\)_\=)/lS2GxBD )]/[xmP+>-vHۉvI 4NKrJ'@ow{ޥzXB\[_KJ|lkOjgnt?5)Cd-tV s^F`bBrg\}ƗO +~zvXdU[zj ؆جۜr@ܢS "8G=J i$"߉yQc*=iAŊB&jb,M钯bCf 塧ZnWQG0zɡD`"wTG2&~F/cw RUwb< #+r@2zo^u=b1084!CâYjlamzAִyΟm$6oؖC$ChkwF:G&9l4F8EARHа՟:;UwZ>V/|mpi"ԫs`g* L~t&'I@/EΡ^*IBǏVQc`O"890N ~SR pL"O#Vh. rH`u@݃q`ދPK0;:VB}GVtڽ='F1,AƼS0'(Ûx Jx@|+n^kLxp Bkhhv7eX{-] ![}8Zv-Cw:\xhѦa##[>E(Ev"H+Pt dim7uTuBH݆syd YZŸKAesajdpjvL5jBL+Uu8 y5w,\ˣzj6}4}`ZqeBǴ,,PI/zo(,fwF4 WA=\.d!|ιd3gESFEnΧtҟ'2f IBp2+kLv{3;HezLSAƝ=Du !cFц:q,'mU;Z:AμJiTֽ7mO^'[L];^v5?T}"9x۳:hO0!u 8ev6ɍMp8&5 q՞ #C?Ѝ$$ًX^Ł%QCGajh ʧ#s)[1v Lk73"s PC|]͗Fa{*̮b.Hp f˄G&y$,:*i /6Ĭ!r)6w~륃xG)e>2~Kwdb#G?1ށ̨3Q1sCGC<5٥?s`nqMV¯5t_]0zAgcBsf{]9WڝЕ8+FK+ VxBX' 4K0DĜ&uΧC=/Y_j:rY轀N#2k㉑҂bsB9W٩UmIAHf؅)ѫ~`o/&B#FzcċZGV߲Nq3@*=oYBcn/i )Mxk5UYXGD'|Ȱ.cC S`T-"GULu 5g^ (GQh P :@3U#b.E7/ h0CNPˡr[]go 1  vI=8`dG6Ўi#JbR Ts:Ѩ$5,&!jU'Potk$I+vR^VVܡzĨ)_f6;/CwX"?[LKFh29p)>E?@;9FǻΞj7$ P6FQb҇Ho"ԝ#j? N%=T[g.3nz#-vmB nSf`%B⻽x|(*V9cOز'a(5sYC)g@]!"l{?s"T1ɀ"/\ q<79)|&U@~@1h!\ml73mxWLABG3k Mcu;2ق((oCEMGuPq^xPwQozJh!% LfG[W+VZ"XW5E+Uݸ(uPOg) ; =T?AkZXɤNa ^*`]f%:@Gk! +cQqnE ^$ *|h`j U˃v+ /jBN}4EnOE悿;:/E04L?JЌ~fym"4װev؞RxuF?5kU*<w+ԧBȻ,>x;e f'?=͹4enh=NŖpޡCVf P{ٱ``5.WPƆ1 Mtb+of~ Ke-v>`y 1\m#bua[mڅͱ+UKҨQ̢b*Ԩg!0m#YFia4_s%8{; Cdڱ쭇A>a3JӌmcMPy$&`c}{w'"|旻m0$I/Q >bBG; (y|\]r^HVnfvUÊi:MoRz.?2q.>gixeg*eB#qlA y4Iqq;S uSr*x$9iry!A$EK#gCr!i>Q'ʴ;}w mj3۲ZOd6TzF@=F$\W!*ׯ,3x[&9i=z''Kqd+gyQcGV >yRE/r&^]`JJlCU\:#{"س4GfiɮO/ R)C{h+w1C7:1J2hh2/r`MGsaetƞ/8zT)'n~ItS :'FwCm̶USL 9^3X5 ]_, Za:Vظl#ø]/yi~r\TNl=ð la-xh[v MP,U랥i9Orx[m\=6mgfY 5GGv3a?IcDpധhXܢ7/e)G{DhCl-1XԫQGyѺV 5XBVSg 5_<[%8(G<=6qH$eГE-CgHz~ޠŻ֩+"yDݩZ,UTn/U?\2͐,y&;#*C&b3yWHI|%wª3Ȱ61Kz3í*$3ԙ6Xu#:s4̩#ӌTdr? 8WB,^VB˨Q\R%gdS kEv71J4ݤĬIM>9ۘ͸*[u{_M~zg#V $SǑx=) ard;ɳnnJ *)ҷoRX-TLGl$YaRf^\E]k  "Ciqt#}57,<ɸ{'eIƙ߳|ْ{Mb9j$Ŵwa@>8ڔ7:"mzu~h!Q>ك5aznuT/Il Rx*يBkzwoYȹR7ҢfƉSaֈXdэlQT2C%@5~Ā4|3:=[$bj/N{g_$/h*&a171TO(U;wO*y]jc1(4nʭz0ҷbb 1ptLLu n_;{/UfJwj"Fqe}U:);Į+޿Nip:=4*(SXuxD: Uʣ䑫ae" ']MaPQ"?"{oDV@Oq)G](ĘU&=@XTG0EݦdXl?KAphzQ^МӅ İyQJ}|lmIfpma#2SDΔfⷦfgGMsǥEDayN#1y}l|'A7K1c/#d#Y%Kёڔ,҃4RdSw [Y."Ue ͒%Ou% y3KEN!9Q2D[/o vˌ$#\ @UsTe5άe\Њm)W/X9|DZz6zX絯g"ʒE.V y+ªS#Y;&qz>'9"'!ݠpr6*ge舳ԣuqqhuj&"i]e"5)RJYS!CU> 6\̑L*9L [ڹqަԉ듎t>UȐ< k>"eVZ!l,g~buKd Pj6a6*G4{DÑL!wiXҙG +zT]Qֱ1 e%Z@1RQXúvYyfƽb&ؓa -9* YK,ݿ(.e:预cM-0K$l2Rƅ Jt_}uK s_#2NoKL4V`4pDhQ[A94Yg|nVZYUO@LwSSf5מZB~<0wpodhq%ico'i2`6nKs5cE<׺C$a!{]GM |m@kcc:J;pXM)"'K%p|'~ⳟh;rI2yjS5ֵ*zngZKqz45ЮM[b7V9]FjyY$mzz|gJl,q}Hmlfb#VXK.PDJs b>;W_|#'WݾLwIuUi06׺-(}XZX#=(!ds"^g3:`mD̼l v} J%x<:m7у >',{$I{ #;Xʚc15gfNש`n>\X`aYKY L]=mh8׮׺CnqA-AcNf>I]&P-PcT)T0cA6FRNRg#&k K zKPaG kH&ߜʑҖGVP8@9O x f߾h{ [!a8_rIc@G/6zCB<9ɄvR%S\Hq|cMg,9TvbC59,~@BWpy=5ZϤ]. CTg3q(]ubXʕvkdW:Ƽ=-oHLLf <'z9 rz)٬IYf ZnAk7NA\Tॾl9 ]t*2,*Ci-4HL Mb7>mZhzKe0 0TAcϩ$ӡ(:)Ө㲆}~9L=t 2]ÆR[rcL Y(h  m-;M騠npUjׅfT ݈OirHGiVv {2vs%,f~Bܼ.̞fOYI':n6ڕtⓑȍ Bkf؞aT.Ȟ6'M]?_baG9dkctbuW!J_c v\a\RsC!#HDB9HL"n@ PKR= iU5l)f[d'3LQъc ]V4ATV&V'ɕHy%a@s*C:߱McQMz-DvoR:>77hU@[u*Ə& "89t/;h7:S@gY|#am^]N˒cmEoJR,J݆'d 7t}($'m8Tm =:IƒS 9Zt֨-3?6%W悍yė6+֊\RCRQ1g4"8U h#ݳZ-,IP1`ݻp]KUmS˖ p= 72= Ec٩}c36Iycn|%qh#e,uq1vAS-DY}4ˎG,e))}1r @;*[iK[I:Ql{ 0Wʝ*BS‚SoW,E |L8>$6Dx`xnM@+E,VpGy2V̕9'; pNcXSUD~:ψGuP3L˽ι$ZuQM~NUUf}I[k@ƿ!5BxVMQ PYS?}R@K#z-.eCT7S"]oA"+&I/$]?Fs9NĴOd@þ+Hqd,uwTOOL׽HF-,7T#{?BB Sޠj,6!ϯq C#gMjәTTAI?QDa^xOEEŬFxWjSo*Yjw˰!"L+.gS@#fCӍ߉&S g%uA[2VV&l}*9XoL!P5^w@zT±dgKSO4Eŗ7jT^FpiJ\a"ޗ gY@%v/F,0Ըޕobh Q,ѪNcA_~/\t2-G7\ȍu&%I"yK&rJdYy9tz?{t囌t3%oxnPծs!6&4G8N0=*KRXr>ӑu֜@ @^1Hh ^)&Bd҆4iL/oD 7=­ZU!nc49 z F OPIȊK>2Q3haB\`0 1E> Id` 3Tc{% hh}rأe#g)m62˓ ѳ=? Gqd` {9 `?9 xӢ-NL6cEPҞD02pj Fhzʜ!͵8Kka8ƣ1=B8eOEIly #C"2y}r.2,=G#,[ZC 9 m8ڄb"'82\?2p h3$8CREJifj;Hct+QMAdDNrx=8% 8G N!8&̔D(/s~'Žh=\62pE)&2pGc- CJ2E#rt?2p. Q4wLϢEA-XƯf#'82p! HQadF>KdGILd`/O8C CƆ(2pH #\d(8G1 # Eΐ,) 3,qdFг_* PldY̝" %2p!  !\d`/G8$s02p68!}%3$?2p"g%3yj&2p&ǐf"23t/2u rg^d "2LL.8&E"ϐmd dIY8Kc8Gӯ q?2G # M|蓋 " \ds42GDƭ98Evd8@G 2C8%dDCȟG󦑁dz⣹b\d9ld\d,/D˞FyN 0:>s1e&2\Ld ٌ Ldl?2p! 1 %7prđ˙ ӌ Gɳ󑁳y~#sټ9rDNX&FNpO܇s 9 ers ad`G J3`E )x ^dV$cB½spbxD3wLdD>B ˹Dzq,8_{02G zx&2pI1}&2ː {~dlGDT Ѣ9jx>B^ 2pl"G &2pb> | sz8FǕ ''AJ"{$2OK#{82pB"{(2O I#{T?2p Qdu!)8DHad`48GAdFad %G%x #) Ѽ2 9cGP*G3yCB8"g"9!18! GgrF8cBh&2p Ѣ1uq͒Md0G8%=r.29m)ݏ S3$"h8cZ '6X-1~d`=x68F>J?VHN_18&gin62p1 |(#;ld`/K+ĥSZrT<8`"8oj8tQf#'YR42pBHi21 sYf) Qf## Fϑ',2rf7 #yMd\Ld\82p.ϱ"ȹ|ade9ڮh Xdl 2OrJͳSr8{S Ғ3Yi|d0_62,1kF$2p"02pH#/2p Ck5fɮUSJ9:Prd!&3V6M {9%־Wvw o_2:)d˫A",$ 1flK`H>xvI4"Նa$i?T=&0f IA['x1e䆳{ mx͑D(I\z^DZ6)iԢ8%[Ok2|\4Ҕ-kvṔS# ~"t=R4p5#B$pws}΂q[0-r4߬C o8wfQڒ'̓>͓*8үytl EҒ^Ig*LX>mq rv9m=\ >Kٛ~5|4'Q~L)K13S;ԏiXi(iy42S T(3UҏNk~nܼ͉cw㙑];Eh`N;,M\OFNk5Wĭ o L6-1B%x5Ͽ!/7ڭVEB;u ^*%}X/LW|~X :1yO>njf\ю$Eέ̎/"J)hd$^C%h|ro ln).l q[YBWS_}]NMMbG\D@g>Y«+Mݞnmo+lyABv)Gmx8ϲ]vҔ$g+:! aYۡ} ^fc*s[}Y9Yc%nWQnL17a1=T5z'CE*Κn jɞ'w_s|NO lV#^bWaa=RW%J`yh% ~F[>AR^@t굗*/ٺ)i"9=5{lmnWKOr;~`兲 i*/jÈpjR%;P΋b1mg.E7a; ;{vz\3RreF7774('rSe1>Kp7)]E,Ų5$`αݛF yUuGӬruwCO?{!>sX^a~@`-Emzi&9ڨM!I}8Inuc 4@46_^|be#U fx掖fz*mHh ^1Sx(uIvB ENB02G14 ~"39_XM5cZ >4iIS]0gh(t陻=M2&EB Zǩ" o!C^,m<2R#s{(ei䰦6ǀc'ا>3ե x1  fݏU"\$2~lI~Sdh GBƣ.@pZ9:Ip-M:e [[9b9ssDOb 94tJȹbu,QrJ9WqPd~4 0M]+eZ苓yXėiq?t4ԁ\thQژp26CDưZXmML /Ժ"\9@[Ż`4KCܣEUz##۸LDȚ`$BrwWlh1]HCz99t{\ND܇} ~A/ T'ꮹh5P0`Ve؍K%4ÇQ7jCg38x3TJ4F+>ԛ6=0JF8u9 XfS.8@!ko$('l;o4Ax/r ?P YZQ,ޗC?kp|Yz֓lS^ 3V p*OsBΊ.,ggI LlN7L]䥉^:UI?G^8ŏԚ5->hz5(2xHB cV 9fcF IX'Ɉ"GC"k31+31+d41/ǜ,r"GE&@xsɑc0fYoQhH:`On?a7X}t/sٕ M֘U5/25Z(x-2 n x,XѢs11Kq]; ,B#[|4oH&rt9?n)^LH5L.#i@ a~lK'qWQ#kC4RQ vaӻ!nGҌͿO8vH2A#@">-,M]`Kb"*}Jt\*\3Ke0ˑ%qZ^nfpju3h"Z΀U*[΀[ ^UM,THZպMeo3^Qxe:-a_ښu=u}̞9}N0I_ZY}|}ʘb!jÚgjHErU͝^qYG#PǬLC&>K=攸DT~5`a]ҩ?7l߽;EAW<7Dġ|eޝیǍrx|\O'џJ,` !j). !4FDj/3Iifx`WQ^&Gha&NC'ҵ^UtnNO0UhZtsu^l;1xȦh.GxUl iC}4npF$ͽ= w5a5V.)FѮa Uy^jvP/5Dڏf vYNUM_x7QwMs0qTJ0#H vܷ68j;M;%L\+3?]BD Y& #'v?iMF7ݣ/0 ]ƪ'و̚}9Tm1,8ϲFː[nG4eX|&HW{Z:LxqAa)?JEzT&l~CO1q25Ir1Râ*dִ4|DXBׁ$hm*ۤ-!rUf8LsgCB]ږAs-=; 俏D(F+QEͷ`bV ZIDɈ}/-o,%y:f 6v %!2Zoc1k|E`GiQ%x\c` #V{bzIԤev7hb.ɫWzƙJ;{c'[\mV X1($ZZX#ījs͇o*{#23̢&J17I@i6EI~͗ dpDR:ALv >R׃ ^: w!~d0<% bA{5$Sm:s G$Ȟ? ?yϰy;*K'ֺF+P)8p]yg+իCM87XC wcS*Gbay0;0w |溉)kj@?idRU.mbifl\wS!IVȠiZspZn 3bm@p8g*%c8F&o&YAl۷f#I^}@:d`_>}(BwPH*[N@lF%HΩT& "N|X!F<=yx+LU]WNWǩG25p!{xK-y8XIǐ$Ruf[C䠭*'ƁF2z>3z$$~JQU#H*E)k|qli9-P2ZlGQ=gZVA{2vN%N[|qBfsvo`zt$uPuPQ/OTT^lZ^)NCT"^@gbi#Ҵ(oѓ|9p<es!anmգİ1,Jy%b%aO\u+RX^W4i5|>3OoEu`ITm"sP&</-2mNbKWحpTG*o = g>0C0^k~$dI`-P-QO^~9 ߔ`kwwȗYnL|e5"h!z w֜f-٥2~-N 4;8. Z5GU:ٷ}Kr ] ୕~v*m7WR 4^-Mu_'y,- 0:K1ifढ,.yJF=j #c0Zy \kZvQj53}L qYe>ˁ? M%X[zh](- 2D;C&Ik zG7N2BXz܊ o32_h Um37hl\<Q1ڙD$F}U<NߖN/!{| (&h80Sڝ |ޓ8LB:/%ɟbb5}N c: ƕV>je2@eȝ">`&o=6_3S4/sf}࿍Nm߁#(~)5nB!S3)L"'v_.)Of334 ULU^4-#PK;&toUkABq"&;i82uvi:2x(ݟUCJ|c>6o;vKZ4`s[ryz̔ hNƄ;oLӃR(jɫSZOU%^i {T <7B~^?K%O g@K<2.xWs4fƎZ'"\X^XaOĞ.|6{L㪞7j8jЮ^ޗD96B/,zuaM :/~m  }fme9 3t+D@9-.scidf:PE@{yu[DV0OW0H*9R/J;G5Wi'wY074Ȩ$zh'c 7X| ;mx}سn<_R ė,Z:pVCO^cFd HU=/FFdIGM3D)O/8Z.XciQbBB0iT&fz'+w2GH4pUxn$,%>eӶM9q8Ĺ+ =Aɐb1%+SЂ*Pf l5@}^=uyeU 8%cS=o.O2g09R쁎Gs0^kRH]I;-c_U;{/T؝Uc(".?&25&騕oD(=vSE'OUop`ZNqbG|}m_otR-0QdT3X*A|J7 Zd{f +fChfR'paU3>WL%Gub9G/۶Zg(M.h<`m=OҐj}XQ 7oqG'~v*6f1K5PpF̡Zv9B|kNhs Lu'Ƌ^𮏃$^>~ΐ9Bbk4fL3Ǫ$hRI4 p)|^E -h>߲l$xGZ yw0~\; }_:Уe}dq ]wۓ.z&pxݭ$%@O3@ӫȞ sPxΓ+5xܻs 'JGTJX ͱ h3g>}6h{Se\z =^YUeT9.}3j"(!hZc[@0 rʟ,)>NĦzg!=9vd|ޘnṁBz'7]HY ^(e=97GKt{=p:]A4tp maa H!,%8in-z:h> a[`&.M']E|7qr5+ިlh8 \c ̷ë7]O_mQ0lj姢w%-HH T=<(44TģY?v}Tx!fP3NԀX̛7qkQ%> #t_9Ec0v123JZ/D^uX$|ܚ4|u0˩-@C'%W%$;Q|,S9FWK)bI-57x@Y&7JNֺ9CRQިhBM$1Ɣas %iUƛHIMԹqY'k8w\8 <+&Sܙ['hkT],) cV FCֶ҈`Yv*Tr65&ڊ~/ Dm]}0UpBӥ+Ǜ=*@vnyÆqސx4ľ8U]J",#M]T(TF[)oWEtIeWg[M}ql5SDD@w67jBmoyuܮ'(ӧ^]`t[en@!PFudJaeO プMc ɚÿ_ۿUC?j?YC| Oykjȯi 1C[RmR:, )ILD fG?Fw5U]+o d'udBҷE|i^).4JP]d&e/>b+22J n+ۤ`b$+OFURBO ?'~R0c؛=ϜbX+ab}r7 ԬgvU):+;v6^gU :V3z{66%6 NAD6~=HRltWDjN>X5IB!k̍XI E,\g7su 4|3NW}<@"`!b> (i z_ \2ro3ǪiqakZ5*XؠeHkRlP_4.9i9Oulwuz4$,ע)5/{;Ys L(=t [t'-(39^v plg1XHlŦ^'eXXA_bxMzT=NJP C/cKW}ՇH m Bq #7.J*&Y1_AކMEg[Bx2_;v⾗M?lWds3oSl%av?V<ug`fQ ^LCeNo+Ԋءw^l- 3[kjhte$t%N|g*xîU&%MmjVrLQc|-Hk<*c?{h/<^+A닢c-F_wgv«]qﭻKlv~@#/J-fy!;0dǤ_@k`MЏ͊*+4 cake#u3aU~߸y[O.⠫a*ec5ԹQvݐ,EL6KIQp_\$+UJdWU-sy3v8doۣb@(`lL]c\*wqFlKT }!f4]7; (L[bp:uwߧ!dɦv3XP9 8%#E{IJC@S=);ʢr%"+ ;©\=tk@Co&=ڷǽ0]x0 n<^ Ɪ?0od%m?7G.Q7Ou =_F럆Uʕō,os88]7nq?C'.?c1ʐ={hދf n"(2n׬(as-_PyCш$0]>M $8a9w\< e3<=ri`sr_rb;|u$a[^U!b^8rE0Ъ|}칻_q.-%A9vNp)ǭB1l XJ6XH-cvW|k&oZTJ׼*0_rv45o;+˭.gKdyV}!8 IHw mk;Hz{>C;lg}CF;~ ދU &u["N18c6'kD_4LU8oWq(НبXX[$GQũ}[j4wΩfearvC g%]m-;ljBIM8!,wnEePbVp /[כ ]"ԅ,@3Փմ ^8[;"mC߾LXޖ T!NUqقZ%QMW oy6Ypo+Kby&;hۻGUyѡЁ"!pbntxZ~|KFRB nnR,?.:=3x$+>` @w,aK $ez=SG4Րcg٦v yTؙ|V>P:E~p"ae5=˿nJo|KK7$!x,?g``wYe s-cUEe+"PY|,$6mVʴd]C=_Ld{NJ&]ne .Xek*?$atB(+BYѥf?c{BJL-Ju-DN ѼT X=| p)quj(vLcoѽu"DtpW(Ƞ߉аDxEu'xDBwO]`qB_!GIk/WM6/'Wer&㍆hK6 l\˂5HjD8ɁJ54%㞫dl RY j_oNL:#rd2 GC/}yXsi{d^h]G9z;.{p-팉?[dկtT@ Gv=5j.c.640xkr-W1 Rkv[4N:KnHX2DGVގ!mK˖Zgi%)p|a> vhX>ac'ڢ~%05 0wMZ$s|  Qx)I E#J'M B>b\dq-Vޥ:r4mR=gEH(!y#bDOw a~)M]OH^43$9uJTwjc#McPzm<%xR${vR:Dl4έvIƸ$$踙\").!ꌵ8/yYh|%'BW{^iQb7Lv =qpr "<+l4:|!@?=;3  '-oCwLcNYBFx'Q*J%JB,vS@OrlS0Dm*uHsEzl|*3=Ij"*owy+Lj)7ul[v-tk}hY2 /M0SY2@/b}48ߕeHl8Frn?1_Hf=<l<|Mc:,v^VYPҸf&Lx[ui'^ sJ -vk.C{fR%{M1DVa!@1xφkl+LjHnBE>IEh4Yj~iQfX_(|nFjP,Ly  SG.ޏԁ˃9mI-I@字.[KO[?mF* 9ɐ,W)Gy~yYD=G2mzD[N׬{,"|*Ғ*mܖ%G_~tR6Ӗu2nPc\cX4Zն<& n))F>].=Uqp/w,Hף&>0) 4[7T-h+:xS2q1,Oiԙ D/|]h8Cm&zp*]bS Yć]cQ$T n6&e ug n m3i^Oq,Xn~AA[B6TM/~pEs|ToN>{DsGĦ&MqWocQV̑1fCGP?VFUb&l$y_d%n_G$$@ȶJ 2Q){ד6YJIf谑QY9K8qفhKo&9DW/`ϨK#XatdF"V3 VCdQa7Y Uh!)@}JWiAfuV˒핹$x49SvsTuOV"|h-˗lk-X u@:4.te%JdV":̗ih@F!E}p7/p+!a.(P@ ٍ/HAj1 (ZSGbqTM+O'KD,) w y Ǒyێ#@+__kΗ] _+ Fdḁ(H]5%/䫤%QrDs3& :Σ4Vm99 0+35faTܘFq ay 9DiQv0n6pZ9~?.ϫR_WH|v/@<O[ϏyhkoXns45gdB!%=w \47-K5p'3kOCB]+&#h1sƋ؂dEAdq8'sY [" ]͒ptgݫ:N|E3f}e5Q'l{֙*Dc4(,j(PrHG)6BM$~ٺgIDf)ё %0yPg0 WYR-y> Ex@z1qp||bN6LQ󚖡@FD0I=s8w` f@\+(pHKtH lHN>#,Ez2U~(jL|ZMA\1S"v5K7fX OGtr7-=v wK<*gFF$+MOMwWUKp_<衟P{m"I-|6Fa]ȼ 0ll:Vu=r{Ym 1v4|V144d{ЏSx d~GE?CJ4 Opi׷jc 9d3E!B$Nr,H74MhR m"yTPz Ww&K%gMo^?-ǽQΠ"^6|Gu uZ 09Z$EIs Lȫh=4#Yij)CUT~50>ZScZ 6u3_y=wuep@yLT̀Xm22𠧡gwvʘY~70UӋW?Ct=.]n)Տ^ U˚Kg)/J& s^ a7d69>[0kVݝ.ݞe5#?Z/f a|zω 6CױY)rS@f0 *LdrZC4B#"{윏=*x<-S; ÐdIIAP@ψH1{aN?+> !̔.xȆCfW,n[TS g=)5qZ-ǔ4|'cz2~OXx,,1q{,Gvǧy3Aa}"ϳIO^v DŽNGul)Axy|'A].j& I4g Kdhp"¨i5E5.L/1`F%]P/4f:RpwtCrw,,hAs=/ivP +3 )D\H,J'q-K༸Zy6.w ' =5hmǠwb"tюiʹؖgwc( ,sNA0< KP ițdd qriZTOGg= a(E]Fs6P|N=P1(e3*{`U|> RG]'N) uadc3f!csڽTڽP> (f<9.RGGX2$֑wIz% h2߰g#'tX]R\#౤rB'X%OJki& WOAdIq !͛|y`^UNVĆ_.@1@XAR dHi&*JMRw<3m0Ý8^NÇ24\,hqӮCtċQZk v;;`58ٞzԉ*c!,7&k n:"tQ?!y~XkHdBb=S ӽǰTH rPeqX)GV-t.s毫!n2~{tYNtnr'l3'6NuPՇ+=(&Y(}DHRH Kd$ЁuNAahU9Je7agg(lP1ENZovm@Q3≭ba,|i83l`Ct~j)8Nzy Ӭ|6f9#-wW\Fef"8 lBcDb|]"n.ыNHIEk d0P=Jּ8 pzκ7ɼz8t.Ω|>yRNG@jJD*)uaq@tq?H .%]x'6yi|?6f?Mm0$Cwu PI(VDfB# a+tn7ep2r dǑd,m6HP(.M/4q毁{\")aHk t=u:M6rY+  *PL<A@kĺ8( "W J U 8'`W80 œQ2RDOF.~8sG8M o$[5HoG#? *#0!;$y:Vڑ((*e|/e ;C_G[e +|xSXe9M^ˍYzpe\ژi1bػ%aQnͭ@s%V`P#)Vgѕa@Y@D*`gs؅H#@Ax EHsi?`K XdyoKG@ Jw0;2"Q/p5x,~GX׿t+P\b@1DЅ #Ro64NhEp4b "Vt!m<|&3?LϹ/|G¶0!;1\w7wfD 3) ҭcKW[kD|J2G*( մcSزϐzV@ Aq@#}o3@BRJRIAC"XI7VXP*؏N?{19"~\e (diZ\Z-X?:2O@j2jgaө n;SͳFEQ\q bM;·^l\o-q^8qƙVgB_5 m"LҊsXuqr@?Lo+B^!a i?HzMXg+"&9c#o i?.W92pʑGicޏЧs?Af>eh.UO+6p}tf,~PukjH NF11 _Jr7a>V8{WUjpHv_,_GS&V+a\0X*,o8=CϏ˅~&^ iEH%S/fskpiGHdwqm#mo}cBUu+T E!Mn=#'~N-ocTp9ѲXzH F0qZ~I}،t㱡֩Ek: J"YN`SPO˹OD0/oJG߹eEcѾroSlG>IgE& I.8u䤺K 5 CLI2be;ξII_S :[J¨X=Ը8n,Ht`IB  /u 9 YYp$i˘zn5pC6J1H)>IP҃@`  DmR[ݻ2`{6EB1jXD6deެJ?)1) }Sqo# +29 t!ḴBs#n }+2 v3<{@:o۽7F6?O<OzBg7/,4 so]S@*trCx=ɈW^3"0b.љool6#e[#. *pQ PI`^ӡCbcL)z'tx;f!"@B" $Ud!YS[G҂7XhD֐598[9D oY&UVv,[0aHG,ح<1Al:{ LH6(R\^ZՄPKZYp`bM%>;Qܪ\W}|&?ABŶumg+لऑfO*<9xO h1?APHz[3ʼ+? ݣI"Y562UL44Aw?ݱ^gC=*#SU(œVzZbǖ-JخQ ZbQvؼi \044TJI}͉;_<_|5F̟2rB L?ηf8,zȲM!DC.9iLÿX8M/se3 ۧ܅bC'-V.}1cݞף55Z4tDLBtiO `f߅%i `d\or6!L9=a\A.udЭ >W(|JAS.<-duUL<؈5yZN40k~ [Qf8qYzyTەR, &䚁,vè/pZBՄ%g6=q'$ý+iyxL p%mDzyPHuyr "Ԝ`R{wʓ4Y"AmAC[/;RVT(toyiYiސ#AuQ4!Z綀1ގ<ѐ#4CEl)NJh*4DQES d>-gʟcNIE}e(LcV"l{lW@QXQ`¤0@+|Kwh)1k>3o8z-h=hmiypS-k^rGg816Ti"$!7K&9h규5MTVG:5az;G{C(bKFuaЬAGP(ʜ±=_@:_?UY#5tLe´g#gdjvQ;z`9yvr鰼9quȕ4ai6e7J;u]wDǥma:"TY:Lm\{%*9ֱWi:.<Mu`}a[]~ö8Dl@YPʶT#BYc (&p5jPR] ʀSBX18;oG^#Z-xa 6_U3Q,pSV/۹FS&@+د zW/^}5^ X Vv?c4?J~F Ϡ0Wހ}x@[W}nwQtq~y(s4{C`" OAmN}M(F1cuX/uϖh;H ]1 MtX5Ki/1_֕62Y'9 9=qT_KDcE:sdQ`3 Z:4 ʂOs⿐Ў=/ﲳy2q'6JeGr\4WfП" C~p){λ.F70.iXBvgRL||'d7!?phb1 l&cϫl-괝Pټ3!%t ת= g{nC 빵P_ؑ8F-ŕ&Ҳl$(g]ek!(UFS,l[+3ϋ J9aHU(Ɠx= ''fXat7ڻ2 1٠ &8h^<~_FW]fS ; y/H4iE !`a\Α)s8ج:5bHr"}77f[isxsV#ΑQ;cwV:wx9m,Z3O=A '#6p0? 8-H.M+<#o\tbH Sn{&+,I^#gMG܀0^ך̔4dufq//* k8y84,Š 膤0磛Q#glWSQqviiMGxQAP9xAǬv/X1lIY5L+9Nknw_tqnw&Hkn߼(XmFT"ߋة7XЙ NҦ*B :R@sk5xt|兺X6#M&+ Ł#Hw![ /`2@}}_7wxdY:": `!|tz2JG<;5.q h^QE-. y[9BTQ5fwhx^ .yTd CHrX446pH0s6;]j$UPvqg~]g$L5kw~ <~@P:* V[b'&ET;8>4rR9E~eD\.6bIG#xwlAb snafMٽX{2Fb`h(8߈,>Vr2g8B* m(V6IyaւgO<:NNe S 4?pO&LQI#Dl(0g IU|_ w&Wfs(G #ݱdZ? X<3Zte#S(efd qq+锉b*72PWņPV)bo9z9꾇PY N$,ouFOl bQ ,*/a2 +Q.R'S[R>- sXʒ2/.B l|cH:Ugu,OT :F-^![b{Pg6= ʼn@atjǪR+P ;p\SRC]+D'`bQcݡq0h{˛Ձ]*'[iATn YXչ0I}GG{KaM&0@}jءsa8'0/OX!kB`U;$~YPBvj3( 9ȣ`#qi%r*&s(Dz݈*"RO 05ަxYЏffj¦Zm`Tmط&+'\G픿%0ElJ GRb8YQ&__U(qcJ٩"/Z @6g,s7 ]o& d0 B%*0wWĝx\8/rpy/؟o+řˤ!#cI E1!I?)HY;fc:H8\ejLAL;?s}k%K\UZ8L," nPcFxgKh`d׼Vgq|8 R6b K ͮD}e90zq^fGQ>OY-p2l\H7ҡ7bI2'k׏<\=SB*B8gE(PW L J%+xmv{] 롷dXQzڐXʖLJY{ &\sEV@835ekG-`iL  CL_iIEV7D#D܂-5vk.DZFϻ%| e m=Akt}8uRlKQ@ּ5]NQLZ&&bueMv,[#q==ŝ$kFFt( ym*5Zae/Mʕ{,-fHZ+S+;msV9Χ[լWx/_ Avn̊Wqi*V13l1k[B+vu_gT[UF:%~:jwTɓD8"X=ͺc`l99&(\_ f? {c_CkK-_o~\?Cǃl(Mm[~7_LRr PM 99Bf23uetGPaS~q)(ocj3P6rp Z#LTT4(`8@j!'p@_*׊_#]"[$H+DOG>!mc= !~hXh/1k>Up!kFTB˛8Ǐ5lm. UܨoQmN~ )z{Os xwcJ5æYr,*Ob?N/{V= v-bw'{ݧ:.N ~rӿX"Rh&UP8O䓼\mI:,.z:ϐ=6_g#k>Fd}_5;0pet̯A?,4Bv'Oұb O+<ֱo滴xSXwhc!,DNJE7p֏rQ3LBּ߭|Ag4fvbJ`h_]dGeh!2j&GA҆x #YL Q$~XwYR=rg ⧝}Vf9\xأUq#>t-pti|wZĒHؤ%(p![9 A3F/`ͣl,ʏwLlp);2A.ItBSx(J:swne>ע~+JR\Fn[͢fPvpNT<\5vQߎ Iw0?b7J;sg]D  [%`o{@2@4gZ[5ıix ( ]Œ$Vtփ MkRXLidYtl)nwS N_5NĪjnK e j?&U=jo =ɝEmV6ͣ>o8 QԚOTo[Ƞ.fFc-(p G+¡ r8(׿w nK5n aLE8J .N)OKB$;n:D Q A 6kj #|gX^AaeFkYREJ9HA(YBa/yN! `JZQy/8h džj  ӧx:'ɜbr/1Jz׎*mB|z&^&Br  ӊHD1p]M D~)hN!,5,!oݸv%ښ"˻ d/ܐUg6\{IA|MOVdkh: os#jȊ7}0't(ҟy2<hX!4I}eق ysgaTE(?>,3VC#~!}A40.6wH裭(dQd6aW >Xt|d4s4%بϡ|&UD/ۺ0thcai͗u3-&_ZuҵI^I| 3 I8%#f'DKW)X) KnF*Qbf=GY0RqmT lʐIFXzsSwÚwoxLRtCeCt*8)Cx}5dG*FL44"!UͶK'qq}_1dA(LY Fl&0%e 9d-fr|̍=:ȝNF't~И`1#K>^GoG4}wK|דڸ{Hj12 ҁcc*GA~2cX)Bo`z I%IBJg_ j'Lo݀|7Y)ȥD}aF)W9PAԥ 6>F ûI/%x'Ldzf)Dts]厀ԕ6w$5xGa>9sx;g] )Y<9Ŝ~a+yMC ~EAgJ6GBH[s)"Mi(/dio!!n%Lj+=F/_b I&8kF@k*XJq^6ˠH߄e+Eds8IUcp1i A>rxCX8y9 eZ: eJ CL}>9gȓt^؁3-||ck%8/IL[ߓUa`:Y6A8yӕ(ΔKĆ{0K2l" $_'#uTqkG~ ;g]yi'KyϚ~c/ Z0 !H^W2y((-݆ qzaYx;-CH:茵*ݟ8vU `z6#BW ~n1y{Loy"Ł\oՏT}/1R2.j|oۗjZMObġe9 vwX}t>Tݷ[3GC(eX"m^jX.Ǻ~PSEB,lYŊk vJ ``RϨՙ$ Xni%I$+qjl=LZhqW~N?a:UYJKH\gAI(1t: 1Dq 6LN1ct-rfq='J-WٺPZ#` "O?J;OCb'}0&R;\y3Kc,s0܈q`|vo/5|%țs~f*ObhO\&)1w o RP4@QD 9\3 {uˣr8ٸL,Yz1'v t)YEG*ޛ!?lvͼ%q5x=){}roՓRIB:FTa."p ;0TH?HAq :[g]!Ď6UCϾYŨ1wd  KC?D_ ,vzy=+S &%c_>`ըquiYOdDBLE R_l싹y(7z&HBV3{-ARVBeoc|+`ulc^P@CTZOUs,+f14lfm,<ՕދN98Lh}.cLN59GlUb%sah%]jN6ձ p*τpK.WBw#N@EyYc',=$m9?q)wcd_eGS3$J"yÿ?=~Ufn^,hƑC7 3hist Hxvd\`9\@SvrnNz#o4 ^nxүV\Pu_SQdg Jqlpǡ6oc ]0qa|V;9rF$-uξL v4e4*FˡʫnȧܶO_A3!R" :AS.Z^_d A^KD.6q]6#oRq436kؼ`< +@|-?_gl<1 v_a#R3Tk8ߞ} bi8Bv0r$f O9yA!T[]yJ]e|!ei PJM ] 6/[ >b+,E60C'^"b"ƛaƆgzAwHA6D.Ggc$03\ΥdNcMĦ%w@%?dgJQdu])r7BfSm{Ѕ$ %-d?"E=8>N"2! fTӺBy" !Sއ-PO,@gִ6z.=cRH..,vTO؁3Oby ճM Yvd 5ư4rY,Ӽ 5_+xޏrӔ%RH? g{Ӡ)9ϲ7-4,^@e:`#[h]*1C۱ct 25H5dE>02J"Uq-+whrFŁ0}g~[F.e sr&˅߆c{;J G+TZNRԯU~ 6oE2)sp-M6fcܕ1wᤣ 0شsFzmDdۺc:UqTg`dq'"B[Qa:1 b4 ׌>9'5ڃ1k2ayj{"@^ey{8S-lt 8J'`u2W+PZ!b?$wrrem8ҕEQ=,NwN7rd:>W㎑jEvjSs/@#Q$g [vsK"'͇aau#{P wPxbUΐd= 'HF\._#lSڿ0 &tTx!!L+}1Ao/}e39k_yc}aǢ4rrJV rYl%h܁ M=ԀG )5k8#BݿBmpA)NFwMLU=#4b>i7])ށf/X2s&n4ܳ :s7\܏sY-t"XkR cdfW3*Bx? WW. Cg'g`c,.Pt.bh Irā"ye(l`0N2eYhF0HWL 5N !Ф aeAEѪ2&]ʌ tRXo2㧿J/F?,@wJ Ej,7n n^"~Z2Bd;>I 3[6BpoqIpNXKA5"Kmn7])/B2LJcFZ$${ Z/\7+Nd&Z?,=ʖSY-f9_(,c C:@j"XŁlA2b.k#Ih?>B,-pHDwfh=bknF:zg,Pd&X_.=֩8@V%Lזϵaws])s7?T3bS3X5$ x|q8\_ N+5ߎ+?2S+)w+LA AEv S]4i2sgs*F:,Wk2==R|'nIHfI.]'WJE6HE vPg?qC4BBS,?f$wtB$9}R> <v_qvx8!M,,\e=› e"4?e]p;*2Tȴ|er5#7yo׊XTtìn6XdA~ ߋupN!,pe0a.c 7 0T BȯɆl <@z&'l}e|>=3T&ߐ~F9 z5_Lw ̠sK2d7>Žc^emYQW +>.x8)pՂJ54ɊCV z*Fv^OV&2<$ ]YdՕYR8MMm=PaX5"q'tAHyZL"bϝO+;"9VVpzk%P|| SN3VSi% _lj[O9">%J䩾VC֚Dv=P"$!RtMR X̑!8Cg G>'1,]K->J OR{U2I|[jM6`vP0>X>A8}vwA dvLr~2dOb6d7ڰi+VflH,؄y#qƓ 3)\+,ZGXyrTܮ燧yo Z3l@jKQM0T4 pv"Բ^F!r\Mw^?M*Wi:$@ȅ?VWJ𷯑8e]`-w}/ý:tORDWlH'wLSpH.kvH "Z,@lW#-'3 ҎOC~0DY౥&bKn[`R[H ZJMOkP5?>~gΐ<9L"χl,=>Pc틡,"`0=\.V >ԆGU ȒhK5rWے$vG@МyI0{A8.V-y*EM81LKI;o8Og6 UqD"ozky\.yܼro!ae`+B L0kiG B=m.s O%'0fg63͜/@d)h,Br4J":e% kBdۀ!;Şa7#'|v(UK]KiWoU^U5 "ovW'y$Kv>GڳEŹƧX`^x<#}zy"xP&Jv .|>1WBe7-1Uo.+Pf}túyÆ$(H(- P&5)S9>0:@oX]R{*0q`R!Nr4hh m.M6QYD 6HaCfRpZlc낿eD#/R2\ћ߉jKjG):=&}"ڄU[SFA2k Ǜ X* M@Ѭnܕ.ǹggl>, +6iYd":{]hh7PKs]1Kx"Djƃ6DtòU]_ӓUlt~k68\Gǣ >&*[ Oms5]LWU& (!`$JD˝W_sqx%CDƬUE']"h=t͆i"Dikr:yp2&%G& 6aH6B}FVRѠdq`SZ*c l:FMa`nքY߈"V w~#<}QCdmaɿtVi^]=GN$/v?B#Ko&jX; Qx2Nc9HF J'd^Ux0,q/saN< hȨb4T/K*THwHCvC6puP M!pEޏ ijpӸݐ).Ξh)Xڰ_A}8d30S-z]"Crfx_tbd*ל8SvΚaOA4$ mߌ>RXz7TR Fr\Mt=&2'J4_sBG#xIjxuXnadU$ccף(,'"'TK<yBIjqԁ7?NEbKO]= &N.lD5B[bΊ^YLlf^+=KJޡm_7 t2D4a,XъYbm +ܔս7@S4%$ndpǑ8e<GBifɡ&~Kw tS)[mA^)Փ# aqvׂȳGYCu8Nnͩ k)%S>*o,.dS8I|үlsּt[5jDYиe I? Cm mǭX`M<΍"k87Ph2vPUTvhp mףs\"4G/AѐTQ1o mPy)nu)+G7 Ecv9 b7ќ |hvH %oNdCI)/=fL4t&OӁ䈯3}xÂ<4S9Ȩ.Oaw\hSUK3{ t/}Z,?OZO`Z !MEo&# $|ww: u8ܵ7HY)eOjWnP/H^uZtU@!owMZ}޾w .#.jh*=64u>vphʕh+BISkoO;/\(1 #+/kJ ԏӧՀKwϤ,Kx[/7D\?Գ0}(]E9Ȁ8`2 4hCJ'M|âRVt`Ƀ,Vh`clNF %A:I@O|ȚD _ ~[xvP%V^&>$aF'A:Loʹ16J(M&IAS@DC٪Bx@" vzmOql<.J!y"˺8p5!2)%bI`D Ý_bܲх՜s#t²s8d8*@  ^eO MDF *SԞ[5^ahZ*FpJUdۡ*ǂMRL O¬Al8MG`f}TLCqoQqp{fJv$uwbGe4E~I$<ΟG6`|,wk =>?߿(ybԞH1#B`ſȳa11e3i?~~kyͦ{#阆~5+G$dyfd2k`c |uߖ TK3)xy[j`>YV@ly~x7KXtM"{9w)tWU[ڏBO@0( $cEgBx am#c2{YbtsS#Gf8y"!WhZhk^ƈ?h#9{(1;1LaGpTpĒJGT"bu}rajUߎPpx(3tN-wCWxl6ʺ$cdO0th f51xt ٿD՘ּ%Ȱ$J2o]Ml  eN8*uWI', '\lZ$(Nb as2J 1`T6 KG6@ Lpy[S+fl3Piފ5MV:7$JO%mDa񝋼EXl*^^<8/Xze] e}vcxǯ47+zFI08D֔fLH5$x?yϟc|ȄTcx3LySdѮڎ3%xl7O3e#f!^u( 3 슶ߜ%7YaS߱>HCr0 Z _+N)0/$5eE^qﺟ5ci_7>ǽo{qZ vT+y[ T`pkVDC[>JتqSES[|ޖ<_=&&`Fpo-߶}1HM"u} \NI2QPVyf-N$k{y꺟ژwռsygz⹶'k{⹶'n!D ᳌8<_) OH%t2)H 4nO?Ƃ^ ?@BDu5зIq2}* ֮^Ƥ^Cḱ4e@ ; nBW8Ns~[ńxJ7F%w*xm3 ]npfxa㼼c3x~iV&F [/Lx~߿|}ip4G …&ĉ/XH ܸUT݇JH'#y$TTcmSJXң Ih?xx,;ƫFg Մwc|b[vv-pA<9\ۓZ5#|eN%1QAGgAVOo žEENÙR9Hy\ig5;5W&ӋK$j҅ę=4T\ݓ;+gz5s@<5[C9=9(8 t]  5Uɾٛ,ԃ&lbf9l0r.eakwYI1? 32F-k{+ґ~,WwjkʵUof@*B/szΗ<|}'i7yƦ?mt)o9U#Z8jzVK'j~ i=5gGd^wqL)e;SBԆ≨WVLbIY$>pL@GD~uFW[:͙Nv"p_$h_Dln"eaԻ|S!^cG1Viv&A>ve!,'U@&8k9rםו&Jp7C :,# ' fYcqzҭJ݀Y^ۍ ?0. $IJ9caOE]}hS`֞5ww iVQtfd`Fxo/Hݠ@0F\΃)ܟhsF׊E"#ri@|IշzϗK7D{T3JtKl7IPe+9oj㪗/ GI9~)fX4'պ"e`CG(h< K)}BoW ӄGn'{ 6ɬ >.o_)O<7esî{™CZI9Hu44\^D#rAyXD_6ķfPzý+ e.$U6h"׏c tD#ٿǿ|=xzl_<Lj E)MLX}׷Z鈼0WñǦ^PLaNH.-4r[[VebCU*k[ugڧF_v}9boYѢ!s±OBed kUn3K] Rɸ8rcq@'AiGb<(Բ6tzxws˶JDL>f#e\.ة#F_0aD3\*dYi7N94'5@¸Zّ.#ʒAD=B[i^( jLJ}*? pKrw}k\`4>)au,K%-A&rz" e~!8lQ=8 Ts)sP,wqGD\.A)M0RR@;Pe*д~0!{n9\]UAU O' Vd')i AufZӇܵғz?;K U`2S!! qeE..b\q{0 5 uK\dv0кe(dgv\hX܇ELwH7G$I# w`ViZ(@B y{1T-xRvYh=Ó749KTD2B i2 3[BCd;E~eMe&%Iғ˴NC 8M$#]:K@oZAY1\ќP{fp9dϷƓo:0|ge،^gVdVqܒ]cW^|X]bj<7;}ўo 7_b >R<K,B3 u^%Kcz CgavéQ]Sw]Z',JZ %˦S6fQl׹! !cj}C9&mcJ"8ct"}uU|DyUx8$?Pct/5_\-"ITyB+)5Bg72nرq*b_joa5ܥ3I /~h$J5 'ev|hE1iBK`psщ roy4 *LX*5禿sb=s'j3/̧5"ѝ YR#Q m-xӋԽ,Sd$,+gujaf SDG3K[87=68sq"WGYy(ï]n|VGX%!Ce0u2q$ZvUmgݛ~crwR1v3oh:"}O$Zf* ɂ(rL ;#Qxz0G^hןHSr=SyR M:䐩|R:З;\DKo4~ S6Q3oa O 9,UE+fiDy!ӵ`Ԑ1\@b/;2zV@d|]͚2{l[:Vr=4&=wV<]i:~9zͅ_pڧoxm7+nTN5u*gDfU|BHa`m$y8Iڤ@b[VJx*cS讧u? 9yau7[{OSiPAr|Z,.PF&mpS@L,0uiaiQD%:wHV/sHjZQ NqLOͱ%9k?Rk+ײ4暚i#{gE|툖"ԸN:YEg2@H): ޥly;QІ:E,~boS< S,cAkRc훫0s`acwL~VL7 c3m'fgq'q.ZNգP3eRϟj84T|G(U@w򱬔{ӵX2 b!)_L{q2Y~f{MB!C0itpb(xiÚĻu] ݲ t9j}f<=ͨW[RMZQՏ:r<ފ4~ )NF>Mu(Nj kNj`Zq[oybtϻrRP߄;a)U cvlW{)1٧FO]ḟbkڏfzywqX$'G,ڴ_0xVeJ!Dhm[CI=_`ܑD܊ǽ%YT 73pn+k|hˊ z.υ/Ȯu\r)WbU[8\6k:wf{x)f:TdӦeIcA6]?Q=nY۫﹝[bU‹m ZY)aI3]Rd6fexp6G~Dz = K$s?k 7HD z y!8m<E|(=s,ǿ*4B]1oAd)Mt [#p_\Tky9>]C&<ёRg*1 GE<h=.%Ā*MLoΈja Ufhl !EiݏcK5FD_(ntQ 'KkKJdS O>bۍ'#A؛Od7;t* @ߋSUh`(3g 14.oePy=>p3S$i{-k 10YQGÖjſ2{*BW-\AEt93rr?ѫUU.! u؆-/3& sD54.\I 92$4ftIcO cy^iXOZohdJgɭ(~qE yk ͮML EfG"3uDYLfG;g/Y%xJHzfu)M nK_*;\/>2`+ 8-V?4渭+GS'f: 8 O'i,K뱼a0]Ւ}N 4Dmd^bDꖇ(Ϊ԰w %(\ 獶|C: 'X32O SZ죜g:}9/-dy>dRL=Tfy+I!h[9NXgý[P 5|G3CG:S|.H-H+phxݯbמlAU"ӿzN@WkBrdK(obLyg+  Ʀm hҍrT}O` ~¥mNywӒ6vmޖ05"$%?ES\"u$^ԇ X.;~LS3."4C)a~7A>r7LGUcX ֆxk9#zL{ǏZ}҉)_@e a] V޹`-wظ~L, $jH6ce/?OXmzh-8bc ɯ"C3(6+XHLf3 xJIہNE|$R۽I%J,h0 ޕˬYB 򘹜oIs)Lz=q}p{ZN2 dOÆtͬy:Jh ]Z]/lўGohcj 9qIfqfIEk}#xc;7LF""OʵlKMk O5䚪,!K`f(_|_7oݰS&#ۜrn!\V:FFU,1 4o~i1z B;kYL^^[8gu/]SDHWs<0+i J`220ݤ_Q ȷXRCdz<637cKpR,hf~6v$-Ahu.RJx@{/Ջ/@ 1`;9fGyG.y7^pa" f_?fh1Tb(خkgg2xYz YBceڱoC,Fq 68􈔞3r WZ}n|ŒB3">i3Ë_Dh6a+M,ݷs-I?Rv; At7xhPj"0}&1ҵ +3i/0c5ח2O^:݋_Qi)ED?t41nitxv$1QB -v}g!cZk9n?w,#n.2S >wpId*9"+:B [yZq)⽌S4`Y(\dn&z=S# +-1xX@ ,zwC<ήQb,8Sm\lr=V qcG4r,i93EĐfr"Vg{h 2j ;Q9*:eu+/vgJ@8k7Lԥ Ny ^XF3a8 t ϑT v Wݯ2r>PRCrJ^?wXa`̳?Qz\j[!tH'Ҩ2`խY]:}Ye6ͮ=?@u?R@<$iTױ;9@0͓FV3 .4')'OV5Smdj%MSe}T_F. 4gh_AK{饆,z(V)T|yT l~==UV{$Y~0F( xuf)řv/4F@]йSe5a?S__BRo``C%D-yr *P"[z:FokAPDw+Ll*/P!Ja zꈞFrtfmS}&A]Ah }5ԗ(> q169rT%ɱ1?S\FJ X:Xs8~ nxbJːR҅APlmxؙ`e-bGޱR,6a zTEL`w黎Xc>ws8Hbq.z/!Sam|w#2ވ MȆ͓yiK[=?~>m= mմXwirωyqt$jȌw%jOB#30=0;?Rx'ۛD')ݍ.=_{tz7$6]X2 OqMKu&i}@!XhT:RϑJLz.I~,[㢖Ht tNLC,CLR_LsZ#L a1 oȚp 'X%DfDCd"r}{y-VU $ƠPV]H9PMXҙ4B dLI˳r5bI1д맄Ij$p/as酖uS'g#Zo~Z}1cm[( ɶm^Y`_n1_ "Jʎ"Q0oJYe64s dzK>b}Qiwn 63\>L IZ/ gـyDrUn`9'qyKlnJm >hz atUmoY,A R&BLFfDפtMX_7`p794 8WI=kGcW#֨)'0b$LJh%?z@ 3 Z =QbĔs 'S2ȎhL#G"р~= bץغy| e{"9h%xe᥋a/i{<$;=oכ2dr15v,-* U̔e<GmFe=M i%۬B;[kwfjnIhw':F`DgBwXJGvL9u.!' A.90 K+Toګ8We3sXGrd#\]K<.S7-`6 RGÇ {΀Qݞ&ẻzh i< bT,o*+SH2Z= M}ISH;*R=G3y}Wz.@kvc{MvVZ !+TE0^4P d"sXzalQ9 9rT;L87[ppd$DyA,=}< 9[E4 ؤqpsrC No.㍴ԫ6 []YX>w4?Dmΐl5jrolHF8z~Wx|gILQ;_KgN&S a|jE`Qc9{x+/Z zQwUu՘RI@XՐx=K ՞ĔIt-;WenFI'vם KT6{ AE,,N{lĜbw?sKBSS,<RLW_AU( )Y(~'oi$ t#o;◌ r֕715#f<|`Mᾪ +(86Xav;R_]IBZ$d\3K%L#,a։$k=д^[v]@KtLy^SX˵xP"Kwr1O2 5+d~&F@0dq`&UdT'CQ>JWơ 0}BGs'F3Bgu] RfAdţsDE)Ķn(5v/+BQJ ty'"ʐyVQK^I rګ_".!d϶9|'ʐleob]reH2c#}Y=}.H}aNB)F:x< B[qv MXa(Pgg%`Xi4̲.xDl.=&i#-*7az;twڟQpyEn+{J)Q=җFOPH@! |uB~PF܏RO2}E(܋܇߇+knd՞B,'UWvV>p:J*s3=/Ma;|6 # "͊'Ȅ3R#E49jQWś7up+!2fU.z׎-Qm%f;"/!|X71C.%]n,v⽐.l !4AcuPY+ƴ1>ʞqvpiCQuP [0ay*ːF+ݦUh9bB~+r@ +QPY|IN87wp*8Se`3VOW'" #ɲ(/X,yzM61ONwMtbAVX16ƿ(.tDA8'u)ZR pzAT;Z#i],>q͍?Ǵ6b)wv5<)śJZaQnQܘrDG^wô]°~QK.xU܋Rj>bQE\WBМXwe F&')tf<1 y;"jQS3TͰŲ> =F.|U)$>aMu0BF.VץB\*0p(Ԁ=!-U\nj{Pf#VWG61"ZE:'D#g{𘕏"u& ܒ=Y(Ԯ̮j *xY@(4|c1 ~m$'M81Ȏ0ݺF4a: Hȶp=׼(\9/z{& fx!_ryBm֑]SrR- 9!;[e$guz* ȑk_LB{# ЎKj>l=7X5SAJZ8q/w W@xTmXh<\].KȂƁ.rgġ72(&cΰDX0$YR|1MFQUݚd`>u7 UyC A_d.]7ZͿ&w$?O57X!孑fhm;c2auך&N&Ŕ3WNƂWc'\p{T8RƘE&J8=jKxBGAҼIj܄ɣR@8:mr^/ؖb=&<69QsjZ KQV5]!tQMxniƂyӏ;=UC)} 6XEZLn Xzz7 䄏fFbYDx V<;t(G圲}%<YN1$Sgj+0|o,auQǸtu. OE 1B ܋LLGth[1X$$_ q8sPT{9(PYsy3K4Jfg [0X 5DWtd.2yZiHW-d%9$[P7 2$׍a<<8M(B}JE׭&Kaܔ>$XI@WZ=ǙJ?cHp)1RQ`zj݀>ΪK+Wq|Xʼn\r 91,VL?4:߮=u]c B`#C98y}6 kF+ؽhOxЈ!ș-pv\"tl@C-"3 Ñl??s#f&wILe4"S^uM滑OH]B94z)ٽi_ j!0[Ft:ӝHo*d~K/T_z5` CF3ibm3}˕ߑd"p:o/pn2o>TXmF/߻, wz. ﯞ7۷ao_ ſ-?\r)^_Uk难4cs]`nӣp8Կ:ma/U̓ПO˟. !aO;mljF#<~=OI^}Ddggyl~//ъy>zݕ Wo_q=c&%lvu ˈ)Uy{ǘeE֔6m}c+ub%,uS|j&{Ίt9]8 2U޶ïՓvOyb>E8(B'qg 04˯_X>/E{J~ʵX=Mǩ3x{H\q|ZuKqtѰ[ltr%_'L҃~# $j-7V %^|`)ԭӶ$Ltc u59SI݉XǞ(H E*$;p_І[Y\t滃8fK Ĺ83k^QN~RLUIV]`Lׂ>^.%җaچ"Q^ }SgzK 2\$z/| ! v']#5OLbH=C>QP%R Y؄y\6x.Qvb/~DlEl !1x^15.dَ@:(qd9~&e{Ƒi.y&"HmjHp2| gP/1Vm@.bSPW?( J2qĆ0LFau MbVfūa|@$9@3k>~a \K@{W1=>l9zD^Li>J`$ v<{ O-<.3HiGt#WSB(ԡK9pRZ1;wݣ 2 DX38=s 2rĈLIr@p%h0mmH˯LzBӪ}#5 [udU!g?Ae^PJ^c?? #tH`Ǖ/蚩-]kb XLW.2L. )t@"IM\U!q0PGcp scDQ˺m^_욮*潔K2QXᘆpDOpH)4t7@ NW#eؘ>^hu"B FHc<$7ɵ̗S\:" 勯 ˑ D}#k}m;"YNleq_خBC_{*5wgȥ19' BԎ]om?Pd|M#Rj@x>6[]Yxȓ~X=(C)!W$lIHRZe7mcc/6axD2|I_"3C ݣfR%q$06&JOz VEv^3U|H pzR7{>Pzy&S|QM$p%9Eҧ_S_ЇrMlZ\LsBPoW24@1qJz!e`A%:Oct sh|E8ss]iL2K+7Qũ܌f} ?"sRȦJ*7ߦxiJdȐ$V7kKi 1F3Z T'[Яݞ~%wN<be /Y|ÈO<#j9 /NpqwCsx_eIo:#XXFpK_A iSJFa,;S !(J)l̬p} 5 +(dA[%f2߇I:+t"QngbTQ~>h0ӲUΝ3z( Nmo;)3b,jI KK=#3ܞ27 mݘ3ΐD3: kY\A{~[)j"w(RsDW Bl}~(k鴔&8 e6/iOkx`~o؛va+ƕ)M@qx8>*CiyLke+S؞<'P4)l-q$^,.gsrd{BJXDˁAOg{V{THƙ9I `5,4sM.-= 7{np0z]^0L}T>"8KB(`''Vea FK=:Yrh_ :;{e%e`\i ]JDeĺJRzH-Do8QGoPԁAMt| )i0iz-UB<(!* *L,] !&x+e%d@~VVQjo͔c)Nv,.Q}gȦ ֏A,>>$Lhd#04 CBӉ\ð9gψ FyHV@vc wnՠipvN*6G88] o84_+,&)VR %ji4ccHU$޺~'5gz%RE<|܇DH EOW=~hr.13UԯQA)FH1;qk$ŨŁg̯+M02w:l;Vɫ}[D8.84,PV|9aQh4VH̵3AMW^B8 bPFx*Aj˫1.Q }R3ޚf/ov_VJ ^/3o~Ȋq:gXFpP4@8 ;eg9}L]*<4a)c=b@c7 (~Z]7&j{+? 㔘>W%(MyŵW{(3.&8 &*=ojvZPc.+C* GƐ!fV+(IF^vՊ;$`(2dDuN69&r2X#+k}chY\bJ|)jm:xJ6mߑBF!T7 _}l//uq5c>N3K:7=\J78k#e}MbOq AkhV2_1:R-btܿU(z [+aQ茎p3Ω/VYo6hn/kj.i7k4qv+JrYqԃ ^Z+ dm,?dPMPdy[5;t$TSMbWM1=F<F `|8I ~ \oVoDWkD"MM.9l8>(Y|C/SG+`JX0bgw S~N"l A%CmMvFSEm=*ʨ] uVEJ=C@n|SLFa# 6G;q^""D$`Е"].?<7lgg )a$1!7j:I6R;'SI\KLSt!EN[c(=q8SP4͌dC/}[qƣ e=CbCH4 v0H?+m"avVXӘi03dA7#&z6np5m=eeI|MXjE,??Iĩ+a'ԭr%s2Hʋq)zfғpAl\ގUCRBR$/uVzkw'6iPZWdgyǒ3*|$1a7fPUM[5g 4E[*$ Gaȱ)7*!*2)C$IJ( }_T\ImIv,,Z眹:Ѝ'}cKbiho?8wEo??!Zv|0(2㧆BI!?-W?Z<8*L>\U0=\EALO"n7ZF#"S08NkէNiCh%퇚$pxkfq}TfSt`bi`j[4Nŋr hgQ=뒭lM*#ȴvۚj/[ E^}W쟿`^v9i o9mZ;kpI&|Vfa.חYpSO}(k)6f+E8q;)i9QpS* M<>R Fira܎ ҜL.sﭺr0ԒXʲ7Vtzsf.!5\RpA)nĊc_҈D pl#zl':޺]i }UU4N!}v6[;Äo[kURYb3ʲy轸 J児nL[M8ô,ljCrBh%YeKtCejxͲIP8-TףDmL[^ TQiOi|* {ھVCoX e CDb2~t8I)+iy&Yq: w~ܝZ9xi[ iTj i@@y py;)|D`!(KFHH(=Noߊ̰jo« S1$ٱ#'uAu$Xi> BR-_.̌D *)Y/v&rgװmaâ[cQ҆Vg7Ilq#T Taфh*[en҈ϼqԎ} 8l?-gbNوGO\hi(^eI5c3 1.?{W %}lkc6^V)FdHzqǖN@yG~0=KO:pT]Si|Zosoi0K0* q+.ԬSxK,%v3КDVn@Ec~^\ә>IH噉]LL5QqۮA@w&"fϔz&Hi|+ž85qz÷Q΢tT882Ca[+ gsG% | ڹ>LmQS^pXiV0'Ѕ M d<j;͎Ok8ݜPQ JK$mWJo&>HLexxR4:j 200_ϒ i8s ېQyDNgi4~( ܮ<[G9452=D9wn$ڧEC FHPF)1}"Iqg$x;hZڒ0D @Lb9}JDjRGC#G5sr׎" ś0DwmF~b#81)!tY"{و\l)l<兴9m:g]$2^҃=ZN~!G֊:%9% +6}|(<,gЧBn 6e`W Shp#Z*Tͬ^l*J{{7iI+_d E`j&Vc4G_5/six>X͒&=&-+Xol to,L@)HX(z:YݏC?8tER7(=I60 x=c#WwR"ں^FhO7AHJx:+VOU oQ=y47R> e:9*?&| q;0]PyAǚ|4CkjXs[fH'UR| "` @mھ}W l&\F̒Zjzo~ÿCcV_fwq"2pnkjka晨T(Q~ r;U3e\̨k`C' H$ m( %K8 gr?B//Fz&q!>",+AGCs85O."4e7 B-@XLͦ4! ϥ L|p$b^J %^kUU\#{[ӑ"`8j/:?LX';["\./F| T=&gq A3j!H6#Vpc/94g0vvS뚚B=-bp3O {@UD0K?KrKj%-~8I:k6F}ܣLJ6jn Mc}B(cCjQ"L8QQ;M,zƪ*߇a+.l^{O<4ݥf'/ 4䔖z 1Vł!d&TC[$;BtUDKiҟ&v]YեX1ZוL\W$F<"CEW*R9u.N5k13rzi^"\+!L3땂Reit;+H@? _3_` ~ \.Zg4]1f[5kYn;z?2`Ey_Uqq'ƣ?Wz8E^-0"F}Wkdݶ )ЇV:I >#"}/5ʼ z$-#PƉ @}iKtmNݫر4HY7 ,CrsѦ'5±щm#]XwI$}yeN.E; &_((BbtZ 0>B?lfi7 *3YWa/0+LFrdtZEWm1(^#R_*mn6)cӌ"+9`3,7Ƃ-Ex0B: ޚ⥈TáR`n1> *.LA; Q^3֞.1 W˶2#+CBiӶR..iQ"Y^ɑ͌YmBEWȓ'ȫ-d 'ظqc10A%}øq>[|1Ê;N/pnTe:Z4,Ѿ"~o~\;z'|M.enVJ}H^%PRc'ɟFmn'q͉Q4Y(C 6TӮY%p~0{ ^q͇<2Åav3G" 5c#R򿪴yqPyVԵay\>TnQ'`N2 PN3#a0ỳٌЊ!r;2+ӰͤZV[XÕ ,Rc`04n\ZRN;rgT"39QI>^@)aU- 2G M;*քd3-bͽq0\!g?Bv6f>h8H$z#xn"Wg`/R] -:U@ykh3O+v>χ:~TA )d7v~#UQSZbm`Gr G145G>CHÓx<'@TTLQ[ކt 0 yU%\RPr?GQ_|͎"/ضt5| o{oZ{u޶ϲ0rr#ŰJ=XT ~gX|e#RA&~k0Bskd,Y;Nf/i{(}YaF: mX@Z%ˡVzd^}gq&qۨmf_|%_*ȷ7kKS3zR(_эuOYʧ֥=*X0d4P $i|ogkKKC nIk\ AVEP;Nivs?R,QD/w+{u_ޥ`˚D42t.ص ]Z$+])/J FmnO[&L|A'!p5W ikL1%t b#,(3 dL$[q6Yw3W"d A+n!roo}g"?P;˺a JŖ(#ozٰ9!Iptkyl'x&dj*7ԑdmP7l.1EY!Cj\仨[&gM=[Ju[H>ckjҚu7[YP8î˳K~fꨂS6 ՠ`32_)8r%0R!o^o3}XX4Xb?6WXupK@p^rDbkjxk ~Gs<ź7cKjj$ôe1 GCog'hА"Ca U:׌Oxh+v}Ч65>5)A2 i1Mf9+͹@8@qJ' wOkgW]j'&2̓$!3 XnK KS53'$9'äeA!i2=4FYZk )t 8rӻ'b%Օh aedʧ@F9 TȷKO8P33}~IgmH>f K[?)}nj~ky^ށ#(4(}E~"GxT9I>Rs"Gt&>r hlͽ{EQ$@f!Fn@,@.G^Hɢ>蠦e$Sez$dla$H`iZ#DĉT;K_$q`nn/1܌ӗ?1J䴄juV&fΧEO3ŐF0Js-KorLۛM S FPqs*> h߽^qm3NY0*;;hFF/*$mU"Dkaͷi~PHl/ǂ\2+<> ̳7‹H]Y@(moy,/-aYb ́#< L\G 6j/_HN z gj"wm퉀ZBRZd0[p v0QQVR(m=.c*)g)L/[&Hy)|z8Lr' ຢAZ-v*6#9վC;pM"8+'D be)9F^ Vo,)~ct ?6kJL Fяj {7S-A<~0>ʢͷ8d̝!!986e@Pm^l<&*# Bf^G(DX"QQD>PDrK50f#!2C&4zCdCΨcHsU|KHcmCͩG\Q%fCr|wP"Z1QiäGѰ#}ɎmS4J8[տϡx1B7UWc|OTjr@#03bbaJRa C*sUBWID쁖ˤ0pZ@o? R: $ rL':OTiW\nk^nM9I|$qZ~*ϔQ1&=O =؀"zρ=CT:yf2I7:Ʃ{nn4G9џMM☬GFD^ߊ)A6mL*dʥ,nٳ&<ޖkʜxj"i>T%I䢥ϓ@ke$fNfگA pC84< V-?|/町y:{~D,U/KŨvVpKxHH=C-H[$W͠B-hnґ@)F%|ƕ;XP61>rC=d25Nm.%vvz;8:!jJT:wH˅mΘ߾խÏ 8>9n;U>:`'uM ;ӏYfNk+7?>֢Padh>%LڕKŁ|2neۙδQK4GiFA(z&ۃgt SaN$T*jfQ_Օz[9ّ*d8B2~6Wh8m{Y%@EwjoiAOBD7XifG, B2چvt#HК*1Ԇۄ8(D`/+T573q@t$njY>5"|$1v3q2 ,)W ۧ_7H7/2RsBp>; n.el`Xg NxCeqH/P:rb]FKƖf"JҝM,FT IW+~"0%f Aʾ܈/SbW2FXC`M$.6HC#(=eK|iu>ÕaP_K71&eg_Ξh~CNNQqpTiSMbg͈)0FzjoHj\K &Xdaʲc%ؗS:ϰ,NaX8:CzףO#GJxNq ]%F`2NcRŀJǍ s`0_G:2 ɆƑXR&ezQD?j eS2+ H@H^HgLXZtx qQcHH(,2>/#2^cbeͽ~>,a%ǵAJvOړ徔D #q[)}""KK>y7&=9Bg B{`ȶKN3hYD4X 4B?MB%zj {ETKo{d-L݃Nבe9"Ƙ?%<6ũa,Yʿ.Id#L8%"pW7Y`Vk K(lSb=rqãmO%;jN bTw 3kTZҸ-qNH>! U„r|R|T2FniUG,W]sw3;wJ̽;%xN0w$^?}N0w$𦺉cl&`fJ}Dry蹨9z k|-9Mg9'3=O3szŶzKP3_E~)@6:GNgaY܁O'`f](Q2y:cI÷&KcH|{:"4K1&rTX/Fl+Sky15/2EBۯ̠ ewɭA!Rf] )$e"o*4կi~^65R+s*h~\{Qsѯ"Ooyď_5{m\ĝ|u3:8%w!\ o2>0eιqt))lcHЧkОy 5{9k|Y@ٿy);[:_\kt1~,Fz%+ ̧Drx0P[N\9!_WFkԙ @*Bպ7 |~Z ?,d^I@ |&ciYBdgdNՉwb@1-UAkT+2&&NZ$>Uz`h[πz_8ܓ:=[1DV ?B sӇ 7LqYHoWY\ه D T&JrXӔtM{6t%T}I>z)8M;u7GZE5"ZI^ X9҈56@| f!`čbr2l^,Xك0 Ӆ8zt7ԜE񥌥x@W7~7 yϕHgr!E's1e %lwm{P} W dDioQir沁G%]OA-=8kB6~$>?_;xFhpl/jZUkFgC{Da|}+CjbE]ܾ wܻW~/A޳}^f|e:U2By^p<} I  @PŮiy0~k3,9A9(:q8G {4qRGs Ԛ{>v_6 JJ~8u6}J!G6Z݃J|nWmTn0~'-vk "Ikj,q#mn~@KH1OChfmN|uMΑĒQ1CdbHh6E M?QBg&܏F#[b/[ۜ( d]'$Cb{+Jp+6?뿩D :-fDP`5ۿ~YH"axgY j|uTQ(e"SEieƑ5z+s{3M+p  O|L: "!u 2֟h6F|W@"Re'&BSo#h8H]Z9PK pXn 讶L%onr [[jA3JUHES7 =dvID %1G;Wbb8J^EDxm>g@dwÐ,* BtZO{ x?LHm|]tܟ}(p tj" S`WA9>UE3eȘǀA>$,-&]%Wa:u oGj}C E1*|kF[MT#;0ۮB6`?We{ {OjvNbԫ f2]cyFWXs2kSt!5[x*VN}FX2Dx6;Y]ծ6 HXRO^򝭑Zmv[jg WQ<[X`hnXnZ}732 gv {^ '∠ K-$L;( H헤,RN f=lKf^ ?]'mwtZ̵ řZfL(%1 aZ'pt+KΑ 6B`0t塟BS#On_npm2LW7 W5BgDvC{V'.Ϗ &jK*C|DzW>MBD93ԗ-xY Fy?#|Q3Z KB,y4BMvzl^kCmYv-)Rfv. O&!$N6cEGzj.ɱ$ɹEq?a2F.28gl\2Ž%[@me2)RNM˼\/&YΈ-D ibOK{yv;`4nv_jf& b. m?ΖvF,_t([!7F}\MF{VMRX,jd-m5_fm:UMfչ)M#8ߩ.%ҭh6 c;_r34iXv04q~X$1_U'0:,F"p-i=Fy5Lrh, }`I"ahhֆ9@{tcxCH/(;}GSW` &BoK;2K1aT!mdcb]?TrpqxH]LPQ_ؒ\IcېϟY4 )RiDO$ة;׏ՋzPU%)dƨ?`y܀XXmYSZl!4/- <:{x@ˮjQUœ_,/# T9ysؠY̊UcH ,S Jr$5Isd8 >S'0iW,.RE}~{0`Y|4u@ JGsHJw{YLȘf$ې!&9X  $CI;$bV)Z~dy:N :'Fb+d9 ϪNXpJrS$PYLN,sp8$vo\ ¡,k~F B4Vs`&e>^7PZx?Fci 3C 6;S`z`ENWf_)A#cQRgYU[zqh|Dïu\6lTV$'>e抐nlEEYI7BL;y4obɥZذ("Ty_tw "l(at亽u{εrq2<3Hh:1j%UلM#V4ʶM6MmmǶ9(Ôf |: NQaUh$3BkC 7܌oVE2'V}օh/6Bk(G9a$To"|KN%<: `5[kb$Fo!+8|Vl$VLٺF?iI),|م9 K"Y R$W~U ܁(pCy~ +ȚHG1ĸѲʬWVM&ϮQ  Y# :7<dGL:#mOoO,7iK96{y D#s(/tFNYtYR~h ј/֘NʰLN$wY>Zuˢn6yxdh HƉ 3$JBaQ!:*!!j¸ jN~J32/!Hq8}mOLǹ B=TDF . ʊ* %}%e+0[a2ųJ-N/s3]Cꅆ`r>.Zfdʹ4]+ PIrA \P'5Nv|j y82S&}Ɉ3Jɹ_7«;Qq0|%@^I]z+&ڂ5f3D rԩ SJ%c L)_E/obf#qs2L 'dn{ :4 coj% M}kO~O% C9mʐn]7(e(H*qKC;YqOzFPy9]$2o!'TT)Lm!2Z"GD) p?c*`md i+ !![9MDDK $2eY1Va'P7 / x:!Uڸv%i,qD k{aĂ)B'c!0 16nՅ2#@ O9O Q{xc"Þ~V$ &P:aZU]Kle؊Y-ķ )ֱv%kDR ei'yD⨭YTւo108?^1^&<ؤ%g]{.2Nk\Ō 5Q;i~BZ" TVzߊI}+]/LH7|i3;e;"@!#@};aK3hF-e[y* 2F ]:X0+wfsSmN\4BŬn|x^i>L #tyHT[P=tPUwNMs-X rxůI;hp@ng$hկz< 41?ٍS@˞gCNO ʼ.v[F-O7z9OJ)g t4_4" (}7=_%sh/bhXݢlg炎d t^JZbw E(r@1TΟ s<3i0X3SjfpD#9hdKQ R~|n\@M] im/U3U VD֕M:<c i.??-wdf.uomXLv4 Ham:u|.F8Ctj! Z$5$cg%K\4i8 iJZ;:!~Э؎ӌoCo af8\Bkj~ɺ87EK\z0gKiIDV1 ?<<1S~/G|LiӈatXBBz,+knJ6"딜Yb:q%ci30/;>UX]siKLOUKnjǟSwx7M/?OQ85 >mwmVZlmkWii=Կ:-t~Tϙ6_T Fo2.G?Hkqi==}i.=p_)ٲAq￯%lá;7;LGtKfaZ07cyd|9\?^A~cn׏7Y lLC[VS<ͥ/$gôϺM>Wn? GQіC҄<@f\(kHE{9Ϗ$/44>SLq< UqnE8CָtJ`=)NToYbS42VƯR|?bg9Z>Zu/ܯ mY ѯ{,yu@䠅@;߹nQFi*F}SBqZ)>ԢNqY^XpnuCB8Za}M&P?<~PӇ?o YQhS/=̣ ՉSgĒ 8X}ȇ>Y-f8~>u2BOmϧ8i7q*U3ɥ/*C' z(◃MˆsӪIG;>$4~,mj"EΧR9Vo9XI 0bI}8 ї_GG3sDu!x8>>i]Ljځ6Ei< mhyJE -yyxШaNri`Msq{Hp[{{4tA'>2T %2+d l *n+1rUATKICޣ$&e2tٗ` JXkRbY<4nkBǓR} D a+[ptv|ZsV1Rq`@1'\hbxu$ ^􌉘8+8#71hé pZO^1E5Ż1@ (_ QP%wQң6K-2-.mDH%'6<[+#×da ,70n%MA3Tm3̠'̿hty=-Qwԁxjvow/mHjN.kNae$^p`W_zI ͆{,<# Ak@.8 Y}zZH":OfbkqJ  4Ml^rv[&+3Atb%ҤC%Q<+/'[$M"ޗdf),FMSyS]DhsgwXx qExa%,"TJ|g)`K@+\r)CKjn#1A7H@Ila3+,GnEK=󬪙+2q֮x鎶db\4k)$WY40Y-z(}S7^}~uޫ9YM2Cw &Y ۵ǑDgMFUrܻb&^]B'Q+<(:ꬲc\$Q$\db+>h h`91^ۿYʢ$>Y܉VXEnpi +خVvrP3|0PM-,/5Mc*$PK:s"[z g5y 0 sraQ eUؽFnD*:$\&e5#g/o)CzqPoE#:ØѶId7X!+6T D_[ʌ̐iZ7佘x=ife &D= cFBOYu%4sq}_lAZOI:(a?~̯sgv?BK㔲[Q;)U~77>z?u3+ ,|QLxW>VK>?aiqߤ{mx70.ͯ~ja9(`jsx@?okp nW*P}.4Nls9V88J[[ѫ DX}1 Eݦ1-V ~Zf@Gwᐯƻȟ36Y~j rB)"᮳UW5H~8d9m:}BK{8(*ְ޶R(r$9r7]^z1sQ624i.D B@3; Y'Y'bGJ<x54_8dEsd;.b5ѼBAV\|1%zkv?m  XOS6l8+aO{ӏy"ŷ[ bBןx沚poY2[f?G,L8% :#yDWy3cpkPl,FyN/DYY5WZTvU9r)oYhpvuguA PY:>ƞ/{.Eg2oGF8nMX7v(c8d80̉sN#4ݠ-Qf/Vڈ [!w.$zBy2ղ _?}}v+xxͭG;s 悱jSPVszN~c<=vᾨ]%3XE(v<vlL].2 L4T"%A:srQ-X9ꂼeo [߁ucݘ #30S]4!0+7WB PV^ b*;d>eA7׀ B }Wsi=? K]ūhZَ, y}Y!\<5z¥ĦsT#yExh55wؗQ#_U8m"{ n$oWS.?a&,~}YaSc{x ˶ˠi  K9\2H?^6(S¹[~žMd?_޽[~.{ʆ]-d9 v{nҵX8}Сh%q5n37v%;>,\OӱO^v$]Nj_w*}Olgɠ)m h"3Z"D')ߵ*OG銭sķIaDD0y ýT Ԝ+06JlL[f4Q!Jխ:^ɄWl|yg%K|*`پ잆8Enڥ9f,3cgGdUh,Fts&nmt:v5ڭ)6 iV8S XWGU ^gq]QR7tc M3J@rz|y"]!%('3kRM|c/Nj@ WehS˪`~M_T I~Q+g銕MQ輕ZQՎS*SY/#ό2ymLZe}ȧ\%mOR4 52Pf-Ӽع-] R'CF@04I+Ti{vGg20+ `ĤNe^)2@9,* م#~"zprM,T>=v G~V' nRqsxCݗ P%egy mS35%䲯<7k]į@;@$; ̠vJ8t :> l5R29mF:p(D6R:u$|:Vǿji+̿@3@V:389ߏt҂3C.qK @K+[T[$}(ޔ]IP* fMCT/wL}ܸ*vP kYRȖH }RQHnٹGEy5zUH새A} V5v|hJ*4X/`z l0|$bIR0#I+`(yj?uv$r c:0 rHɫɨgr9#H^gU+S;QA$A2!K_OxЬ0y`W7%@KsMkd($>z42O4_']֚jg.J<~lF!˔>5 rGa6+-1bzkJ <#Ywmt?|r̀b>^+Dp Uu=r)9EԈ)Ћ7 X1]X937܏9 Gs;~[ܩM(ɦQz~;yY+) {K.UKwԖ`~9a˛h1>A~eZ°icH x N\.f.8wh/~Ͽm0T=ʴ %+~m RjMܺ jw=HAn"`{(H;9J,'IDf@Q;OZttU $H"T]VB]*"%Q2LU(%2D43NqX7 e,prxd{@<: D^zԦ2gE}Ts`ϊu rk}◭hw }K dc]S!)i>9" ܆G*.eTfm]63DLѵa¨4-‹o`9!b :LbF #ue*+L 6ۖhY&fF6ss>uG Y$PwǩR,Sȿ>hh[¬3J) RIS9>\ee4w>f( d^C8]Z=PĨ!n~x^AE5V^j~:활1h,|)Tn?e:@6ID#xm:] ?wŅD25)&zάl0LΡ?A6<kσU>T|_NFqkٙPi֑΋`s5SJL_w9oZzwVU tl`x%<5k>Zږ="l5`,-1|*o8m\(ȿ-UnscXY׮(tH^eDA4cN.?#a7 i9(uBmhfhtK$W|(P%4gH_Kxfvk_00Ww3(MXu1cg bs!3jB.r b%(uDu;tfwwP7.sl3P_ K󐊀CȄCoȬ/ Aɨ-D.8Np NeQi |h͛1bpc OmS|_{} B?eW ci"|IT2r"AWB8Die\-1lbL=)KU}dVo/a200184~6 ֽ, ZM @lH1]*> -KSqRQf@En{%q^O4Ui$4-әo=+"yq>m WK7}& (Wx'*>K m$V ]ULvJ M@J͙->hyψ 0ͮ:> 9Io}~212j^sMD$XPAmli&.k$vcUBy \!EյKɍ? 2r4!6 HkkVW}y~EB x gJL2>m/) b 1}‘T `Tso 'z /LTrlXV`68^|kKJ;ڕIucRTb4T~TB#IbK.մ# '$4%FNZ.)_`uŲ8G:>ёQHi>,rpcJ{Y'ѭqn*F43ct ጏ\oB5M9I{8FO6PXgQQ,&Lͫ_H|F~ ,Ȥһ0Q bbw!oSqႨHh$~ϐ?2- fҖhĠ ՐI{͝p4>%4,zn^}IE 7?1$:M_U)8!CKvnl䨙~;U?sj!>]cʮoōs("@KT>@?np2KW@la\^]-u|.qE>v9ufz Y^4s7?-eS,Ӡ}l^%ҭJ<2vh'$ȰKc>ސk]?$=R I;3ڦ6$?``pynWw(za nUQ9!j韼9SY^\wf F^ҝf"[#h$3-VtXc/^; <|j0s} e^[;4ʘY%I/]1@VLˎ4c0[-Z5.vk4J:{~FVt,E _$8gRC m"1ugBܜz`tYԸ{p1{ZyX;ңximA\+cy]c^s亳ޥ`③h3 .)eKyG5tf Ե7M* _LmˆScu?|R"~8AX`cn< $w movJhLg~x=~߽Wd]4Fq!/i_5`U:/C-bQ]Wt1UTdy7w^0R-P"w/{_*dvT1p`fʚ q(΂P^/l0S8ϰ!bMFNDR; M0a&^$НZD+@q ln9* X7Cv\u 3k8O ՞SfjԵgkQmUq)-y /-M^G@cr߃A5cMrEp'`Yk x RG`tg@m|RW3P|;p8کz+peF6W$h"| gtJFi"̩IB}Ԁ~B_,b\C3aԧS?wNkI]uFГ0iI'"0PTXG'3k?@a>--29KQmG#' NǛ.FK'ٌ&r8m6Jf~v)`8Ԝ ͲB-ǟܾ\HV -G0e7pűg/6_cҭ@F9Z~ PGmMނj%aid^8lb]V^[E4t21 4/&P(Jcҷ\>z@{e4a%9U%,G1xq3ܓf`d|I'o"}sܐCwB{ ȩ@G(0R<$:-~&"[ $ì*=BhՁAAGbDQL4\~4&9#n^1[Xi A=\H]d $BnbwJܑlIBMw "<dKȘhU<ޖ0V"{{j 2<Th`|$FicgSNYHmSp@Sqms8(n{AԷiCWcn@'];tW6FF(f:_!Tg@sZibBDJ\[4ըfvG8I7\,dڸC5J IitUmO1ՍxLڑ.ep7!CGAWJRעyM=㔟 JPN1I>+ >F.pd@f4A!l=h5'Fڮ8z:+BY4N}^!&ala\+A8J.rP B[Jc+AFsCHEdKv3tJCɿU STeB$*!S]ᇑջ YB}*MC:6xV`ò퓻CDB/ n [ 0X\˪T-r}̰'ODྔ8 ӯa,X4Eh/h4Ăɀ2`O|!Vѡr T s^96:m8c!Rp+ՂN)/^2{5iZ*.Cn rRy2:B4u%,מ@0G`kj9)@Ƶ`unuOLee3opiη/}>%fNa2UHJ&Gg3oT$ %.s* jԮe]RAI9\#˧+ IWok|Wc@q p߈1E_,|$_twT/%5´d$qt #2'.Tݾst㍓Z;C'+I릕B?p8(^e)۰B2B瓴2_2],?$AE:Ox E2`Pզ die1_iCnR-V1]*?h CSs9wm3mVLȴth, .>%IÓw3Ώ/˳fPotդvs$[ўbT3g-!.Dixxe|9J`}0Eg)W(2 ~RH0I/ "%iѓsܘ8FMKwѦ*cv -SyD+hƝ|2X40W|oZF4⭹Y-@J}(Rw $ҩ"(ڼE'[<k Q5X@[S`w nOyEЂG,1U.5犟4tgKuQJ8@u+gӬ{܌{O%}Jy "G/ۃ=)[3m7z]>q@02zzcg<6T||jpK!1ϝ '9|t6/e\2e?vyZ:Efi} ai]f{Rz Pٖ{OVxs;)J'WvЧ)b$/KbT>V*Ui{tGLT.8٨3uenJSv !@Xv,`7aL,~"Ѡ'˂ie"P`}v} `S#f1ɨ"P%9Z Wb*UĊ@WXO ,J4ͩ%.`nm-炶N xfR3H H.XAP}%`n-P{м Ie?+2ˣ rוҢ MYmB\p;?O3z;? :K&`Pq\,`n©hs5@x=]?FXŌMzdi" o6l`ZL<3C$@fv#7@d/[2:~S9'EJ$v]ޯ~ʑ j&ū.H xtǴ 3MeNos\ Ae3+iYYS0%ѐxJ!1̝yn0LO>IA̪G* 44Fg)>V>&&yb2h˷03ܭK o\DM-2L 78i0+o q&#G<sE/{:yõ{Mmw+ڙ:֗ Ŋ^ c. yvэ9'^R{Ws^Z7f ,gKpI[\+Eݙ=vAx>oam̃(ZNs"8M]AF+,zGw)۱$c)ύ1LM6?׵\\)=sWڜJ^B(czWIW BWC_y`Qd=g kZ{>N=R&zQE_g[^j`M{3G-xEAJ5)Y0tc*ttF G^3f1%yr 8l&&+q_ٌ3h9fZ"a8$"BxnGiFs#xz,`;E򀩓t?)@paY.tLZ m{ F$W<ʱf$[cGZ.@\]/)^=RSHIT>Kt!ޅ%'r`\|ut[bSb"rLX@E]x``cDgHt0+YdfvjU1jNkv[7/G5+EYƖv(BKx/<ϞYJlq q6P;b nAwƫJVp{<7[d<p;(]X|>^H6q 0f! 6a'.yě:" F%_8M'ܥ7JoI}bvhh UpEMӾz8"=f^$uo^._s4%Ipm\x~!Ľ=IŖ,$|}-^pz$C<3GjA;eovjy4|}tY"*%u7!OzKUNL_";+2wH|XI~Cugf23#a]RK5gP.pnh9lvuA$PRN0kV /$>HT޺3Nɞ\1ٱ<ԵQ8>5n= Hqïg ۶Mք& A: 5ovȗ?Cz㙩7Yۜg!1tCaL8y23Mf4UQ .Oeuγİ-5@,z/[r_=3T֒if^G| P|#_Knsb a T˙զ=K3;Pj: dTg}jYxCS;1 7i@RD QpZy|7ݨ]`WO[Um!c@\XXb=mŘ|iZ)8ۋ!VuQ÷B0{3yQÍ%AtIR z\>Zħ+R7ˌx$hչp]fsP)gw$c(w(?O>W3=GY3dm[R{bgh;3xyňAD鈑jT_Z_̉#YJ=) *=xx5 WS6JS]!]L\ôrŋfb}Ί&>Y~?lY ݰYaÏޗ@qnoznLF.>~pG_؇oB890dp ``7^uj$ h/%U8eB\)-,z'EfYbvݪjv Y $RAa&bOgSn  ?:r2)T ,IАT`(=}ls2K-"nhAJ#'j&PN Ęo˱N,>X0碝hp2A}0z >/@|n@3ҽv GЈhzN[ 3Pd$%D8ıb|b jNL8z3afA,[&N ^} %jG\P:e,.:Z *C3͠oȳL!,4;1v ִz5E }'KF8^uZZa/?bW)ܑF*O!xcMɏGEV@ "q:H$ޙ~7!ɜ }D(E5d_-؟@x̃{SE)&KrﶽOwsۄंP7ߣQjX @r§^O`BcI7GIFlHkp"k܎I+iIRRSq"xa>@XP̴]^.W?W~ 2W}A:|% -et2YG2^P=,G6bYh I9CkW`spI (yg&.m_<+ @D1 t23 C1؇x<$Ƣ+6A^YDưT2DtnkE>rdy5hM1~qi{koDZ بܶE%D;v0Vܱ5Y!w{u?+lªRװэsKPsغ^' b}.x]ouiW @ݜ/~;Eq;ΰKƏ2#a-p&ns`ث\[9- D(smБ2iߨb+12Gf⥪6Ii`b{t̻O"Q1R1|_?I4F }@-m#]{ٝ+В @+#<J:=VljDNtD% dA ?U|i{(_z;'o0BxaqzY)l&0(&J- G{׭C`3kY $I6uSה (?;}s6,j/RT%Q FKs Mhz=%IX(%`_@Xݴ"肑1jJDjVΏoN}E;@UHC|oq .!PInB\Pͱ\O XIkSHeq> 2@Y?7kiEH)ELTI5_T q SvG7xV),9;f~HQ^[""7*gG l$#\bR5ze|Ckfxڑa͈ 4V&[E$}]ղcP@06KU#5rX,pL( @@Ekt,JrMV3deLy^00|FPcD! @WD.+MH̥]Q5 kRzneb/5258 -urRg8#^X6bϱ|ll_᎜"ޓA@*uK;R}v|JR+!MǓu:=TfW|t;\ԋLjq5F)bQ2\#JhX VFln̵H X,bZ%={ Jq_UdAQ1q'ݿ;ojBK.sgq@͓{i}%U9Eۆ]izP%cȬ4u%УH&t+Θ(bW f}>on܁^^C+ljr5tsg6BM-0xus͂oab'#&QfC7?-a]i 4i.eaGa<6)L~ PO_f`j.-~TF+0h0UKbbVwfyh zcY̵ U Qlڥ88츜d.ĤPvDvu,o>آU+P"1[·l.w a(ip b'M:1>ŽPJR%rž2bgR}GUmEpBMK5"\pH7kgx|s&-!Ee|\ddSb=v9olPj_JX/ցq"K;-^4NDT(ǒqL-vap^a5h<"Bvb+U@8x}{09y.6" }QGiZ@Ijͻ;B7#L()Ib[ ޢEáj %۪':8?XApͽ™;/FMt(_ Z=vi%ɳX%ҏ%Ԏ>A5C" Akǣ>n&|ZBHXqVP߁[ d@8EPe' Tu& .gI`?GʭW*8# e+1SE =Sώ_>{)zM FIKWdp|dx$WbWIqFZZT'ddu^ 2NB+gфc36[gYsb'C^ { =K2S~m~zyF,̟9%< R$(NRa]qf €F,QRcjZy Nzim-X;0sqO|S49[!^ z^<s#l=+(1TP"-ll&f%Gs[~©GS@mr(X G&{ЊXk5-o!-]!>&pN|-uϝ 8|\|j{\f1;TEA< 8eQP5TY OXIB6Qq.9#+p' 1 HY =Ĺc9ZaK#G{)R]j'v.-CǨPf&K?pGy pa0Dŗb VN!isLnUe]cjK/UZ r_= tb%$bmoPR倖Ib[JU!bA g"_@mCdv,!ߺ-EɃyBfiϣ[ཪC"Xu-:I4õȚ%{\_sL;(2 uG)z.wY"(aq_d5b5q| u_82A-Ϟյ7qa1qn&xoP*OlEdO;pdFX4Ð 玢M{ n~O7 \]a_髤AO,! _5+s]l3es$%Lh"Ybš&'(M='M (AƒX27h )y%mHq)W+~`T/!:{8gmUVr&X>!EzDpl!1RDyu@Cnڜ9 Y7ֆD/HfSP}2?1;0]38b8;`_ a^b)Җ>mDƾ7pؚ5O;Q$0/v@6b 뾨&  oF@WOZN6,д( ip%:3 t a #\@p+QF`x"}&l\G %$g(FmLP(5G 7SZyΉ1 XZ7@t(QiD} ~y.~FK,q22<&##dTA:huDκojkJa m!5!M/h:,F+cdl2.n'禎dž&ho2;8 }s Ϗ6 >XP9؛#$(ɾJ&tx,Yl+*['O o׏fx1Dx :X~On}VyUCʶV![&b翾;%3Ӑc"3FJV HXP},KlP@c״p pwf`Z2Q-Q\|vX'{sFC߻gʼn?:e^T_bbWdzA5HhVAvH *̝>$7!|ֽ%LjssM`Ő_``gy抆 @;^CUEQ3.5sDiT=b]݌os|]5ypl~Pcrӣ)]F =W*^C )%9Ȟp*]7 и$8.$$gB̳]֡*` t% |zu\kf3C`[?KM׀|^ 7 %Iub5R>5&dSַH@ @wrZC>)Zp7aȩ5A'^e(xq{,H^ۭh) ;ZI"#12HB~^aDz" =qnQw0-)f'20C!9XxJ˘È﷕BqD`"0"RDЃBP/!. (3W7I5ɼ &iZ|ԫu%)M5+{fjM62^H[3¬tA9g\B0g&3,;ܮs0<0X2RL];t==qiY2O!ѵ/)jko4-A5Sd!{޿"ZyZ@1 !VsH% . xH;#i#PaBCUʊL43Ǿ1QU$u"wl[CvsFW 9qS-FtО -?Feq_'eӹ8wW lB]½.#B 4ߓa?#y"YKN3AKHxU  % mUR,ѻG@RV6oMҞdT|@ϩs8Q˹gV(X@r0FsJS^+N-_c:RXMvx>HAF*tʑ!jhEEn k-E#/طHX`0Jh\6eF"p/`O̪=f"6 _ Ѻ Y45t|LH1.Sϫ=54TfJ<Σ5|^'y&ݗOO[5:ڹp;sO_K3oHL-i)eG{13'`&" 6,1yKvX0]ۮ5fE Puusk R`Wadb`Ljk?@,&Õ)n* 5՞p4V^,TUaϐ&JSQ/7df'N/ b#E@(4N^C_UWk¡l"V:_JH R^(M^Ix܃ҐRW=Ӽ'c,*U* "*(ȜHl*Wы{0|/(t1Њ W/;{l4x5mDkaʡ.K69|/U'BZSV?0j!r42%9\hܹgQͪ.NlZچ8jSC+0bjwD<12!Wa9Eq??7 z^s)I@G᱆PÀDG [@?A',WrP\bY"0nQ#PANxySn!rVOKe~IgyM-blKnR([#_ǐqmj^@N9-k$kEzΝ ]vG ^cnKEn3<˻㖵+)ۭ>OmfiU5)OjvK8^]k!dFj*+_~8yq5@D(2`Ctshg_.%Ky:8NCHTb6/f\P{ҳ{Ѐ +f3t+<l PjVZLI!-\N}"`+2t QZ3dʻ.#<(fL1?K}ژc CrJ$|zT0pTJ.K]LKa$%= 2Y(与hMHDytR#e5qq̔-ӤHk~ʣbY}H>k蜟-h%k6cloݺތD rDv6MTiT{\SZd`N^t0%dڂDİ Yt6!t~boP4gy0 ^17PEqōB79 z-=hK:<\KU#tL> pQ?x^?:՟]?.OؚtQtz4Udz_;p_p.vXKæ_)"C UƩ (}[<YtkY8RS>qq"$¤o)3v|GP%"ZɄa*we󌹃b !5\J0j;xԝ4m33o"M8VJ<82wx)L.8ɈhJRyZa硇GL8N+ 1S{ oo_&q#5dNvIEƂ]|may2zIlφAƟViO1AWr/s1!{\J՝!hgZ\[1yW'gy} o!9sx[*Fݦ+)`0!+tk B7M6޿8v9Q^a>+/]ᬱ~sairPqiڗ3~wv).-f@.Sm1IwI?b "gI$CMȥ<--gK&hU]opv&(aVx:2%z4MBq#}^~.$?A{hh% Fؐ m-j }9G*ڕenuN<\kVE㝮'v0sul) [o FB E"xŏ9)R,*~%g8t 4}G1HQ4HO> P7HFDQlG{XrC;f5V>["k5N9l7}+.W3,ϊ%4r[bp7-l (/ 2x[){,'&Eh/N< [TOHA[#I"R:tKwa@Ss}p;&4P%㐸7_0Ĉ XI$n5}}XUg!^pTH:Y16=7W\ޞ|8+rNJء5/RR,T{,zKW6d%a&eio^L~[  H<0Ӑ[e9dv6JL#z{*;2@[ELUy98ҙWrboV{{  *Vu2i Lâ0㴥Z'#"b&:]e`g0^]HfϋJY,ȴU!9e(ucgGFu\aVyAY۠@<#"!`|Jd8zuDl^7ʿ D&1vd2{~fg`IAXhRȌĈ4%I% PEHdv(reBf]h31cC,5ft3=LSʬ4DC%3OdvLZS=MA|[j.`2YaB:+"âKV0o $M~;__dÔ'±i0O4G/sUCPn&m2rn|@y‡1!pQ)z2VN~MQR=mZ{ 3cwV@6HoX C:( a{=uG[go0g@]w1.7Jȅ{!ho.(ԇ>O3AY{$+ 2K/ [d+6㥓PE'G5N/ёn@"bW51)U\8SY3eO?$O|J REz d\9Φ;`cp&l/,BӸ !B=-+AR(I0`-UgBRuQ\B>$?''gT 旲kO%2y+Y5DZ_B[g2_8/\O[K|^#vjY[40X #(ۃ *:sAS湠@FџsЃH$H|4h%ODRam㰤$\,x`گ_ʚ-sٽ ipkww}r A \4fLGvR1 Yh 3[K dluh3|G.У,oj%uT, D?T"}4 %cF5lٮЩ5?0En5w] բJ*R^C ]{Ž ^ܔG7#vi "A#΀ns+zDF5Dӧα/+j"+M "lDwv)wLM)^Q3dz=8eZͿ uZwNzFԒ% =-P>t@6{=DV?IT`__>n/s1QL(?s=6P2p>(: ^(]"erY~>I%#H tCΘ<Tj7*RLM dYW%'ISZP<2|fny?i>Rv?FOw@Pi.v5Hz/"DnA O D'\U4LYNKU~8塚 P@lpgG^U ,tďm/Cj}\Σyu1|Z8tGkGsӈ[*-**0{'Q?Z :KLE7b N*j7i)J sˇy~+h ;xKeRtIh%DMDA!H\$oܡ1xHm  1A`I <"lgxFݟӦ\fJ:>/')SJ↢d݁ \fǓwmm0Ҿ=K/EAĊPP] ,$h)g:EMlF ;IAc9wXc7T k*]r)+Uw0v"G we)D*D̻mH?lEHk-;6]1N `:ymӰ" {8'1) ~]{r( /ԅ8 ihK<ς܌KtK6TϞw|""u0$z rhJklNc̝k~`d|6i(y1ȑ17u+`&_ u&ry~9폝*J+řukvؕT K;GǎIE-p0yG 3x,uWhO*sbr6y JXIxj%GEH*!:}Zġ!L\$,s#0Z hbm~q VPelRJ1Cf"+q *̍.']zsS盎@PC2,QjCa~$\/MHͱӕt?ZI0l Cע86Œ3 |{imZ69 x;f9/K']k~+3o{M6`}|L||F;~\BZ`}xv |[Ա-"S؉֛bJ6xd!uڇ]`8q'VuFhɒRSEK/JW0->EJ*il߿YE/?F i4uZ! DpNT|QeHE2 ԻMH@%YWP3_]ݜ8^h4[<}G= a&SFJFBLiS;y "x1m)B#l0b=#ucVOM&>xLi[|=St%A dǁi&9eƧI^^8 #rq0Ҟ^ōԋ a~kpCxbby. T{;M }pu>ܿgd"]xp*u%,/ٹw} 4X "4d$6$0{HN(nP;ѽ!R6~&#$@4ټ]j,IU `%w64-df8W(3V^pEQiʞ fb˴SZg-h 0:QA͒Gv4gkR{,ss 7 rFh5sr蒿dvb(̉gFOwQLpY0v.b˧^X( ulz﵈E4*?Ș /) Nw,R"٠A! @.E]s9WM!M/f'#|A`!naOb2X½nExRVި|8A(; vw)KS9 qm?ͱ|px2&qz 8PgS'Jծڷ[b&7Ѣ>O>6D(ʸmWȴ}k@iLz. !R{t5f7Ni.nQ!t#yl҂bf2zGoBs=ROZ/~Oò<ѭY6Y' Y$H8@]2udm1 !vq㕃."sEgR,E}}q#OEo!EpheȔ|X}%Kb@ IzdԳpm}g/k^)*]ϤoH^FA}Aq*L-9d$hPIΛNJ/ 26B\Ę25t8κLgP.2yʬ4DRX[@i^h13,d4@|)-dɧǥRmupg-V^uT07'v}umY?D i$ƧJkTbG[sI^Y#ϓ aPqshvm~yUEtמ*G4ݲ$(YRe¹=͖19 #lqFX F9c2/wA~Fi8٥%t.Tq3d#PUOe8|v֌רoVVķczC$!*2yz֩4>5L+ʹؾC`;内v[Sa@N;< QY&+4cM2϶?)y~֟Ҵ(-/C:~>^6:eB$SYJ;ה=k`T pHDL'`|~0.ZNE(PsMؔ)l]A h:dxT]A?@*SF8%Alϝ җr!+H1Np}m?k^iP!G,W3{b܁T쑚۰n4[73J'!S D!}\ \uJ9u"ǒN$`l'ЬFP?zswи_!(iuW"x|hhHRZړCHѢ[FB#J7w R)r%Mz &;C)H6}9aU{4;v$s7l?SY ǁ"_)N*!B#ـ4;UgR@ FR?1; ` 7,`I'L;QŔ}ĊVE?2]`9=*P5( .،hd-n T!T* &"v!}ÞWҲ{Cڔ vZJ|zGECR\.diSu`ƒ͕ioU-a,s'Ll>۫#2ky϶~ 0[֤y زɖ  Q03ڌN0772}RD%bs=`' 8<ֲV8 XDW~ Vu:vs;j{K\T5"V)pf<&<&n*fS]~1nj򗈹s_ITwI^r~2x >ݦ'3‚!r{s^tCЙn&#/x8cF <_f׋1%R#R9o\p\ k 4RhQ1_(CZS-GD)xgH$%/$^G6i%^Y6TqExxnr?@`ADk5Ɔyjw*xØh#`aO;4`тhƷ`_Fx!4V v=IP2_[QL@IP[tgqj?_0DƉ[d זRxTˢe*/ '7{OK_1`_Q|uH/iҨY$2~ +TvOl K QFuq`e\fU1GoM=Zx\Ew;FOG %/^vç΄L*k jj0ji BԖIfۋ=< #u=m?săse.ec`Kܦ&`f/m |B߽ҼQpx_^\\ˇ*uK^z%e=ΓF}XHȋ3Oo+S, X/y 5DJ6[D1u8|~Թ˵_0//arNi?y,4:CjeRf4_%RӮ!7×όB,7(ᬥ L kzb:i݌Uo\$p)qZ%t_[z) ̕A[b;?eng2h^,E&@e6|sDs9$7Kh`B}hS 조bL?|t7Dj,k1߫h"߰ajAk_\Ȩ 7ՒK.LfZr ws-,X|i儈Ôr$lgx4u_YMS\cP,/hfz+h|w+1ֈ멄ꚠb+4Ϡ]\l;+l3Vm=?lxp org+_&,^E_A5cهtK Tx ǹjJo;PwgK"Lt(Y._#EW2fvub*=Xe&a?^i*Ŀʃmv!G"`0-{rr|&C> ̏ rGO0bFAtGlZYC5ޙq吷Y"f_i `"E ʱ1(MJopAiP"zNE, Y;`w(;ak؈т@h3nܞLkzS@]d?r4.1Q* .~|s~~ % r@r XAN1Ѫ;Zn?qʪȰƎTb zW6wZ41cyW@&~iPx6_/cִKB \HvZNd-/y;gەQtŮT+.= tb?exZcDC/THWVY?+VlD/k; {齩NYL`=(U8y2Xw6]G.MoZuNp}Q;E "/ d->(<2tZ~;5{PE:RW˓C3. KG˖R&-)b硿gFY%DX:J uQLNR ouWO!"mC̺^sP,+$i;_K9V .?Gy8# {\ }#֜8ld4#)Jƶ/ݲn.;Wt{MjWK3C%Ȃ6|ak->:qv^FQysAJ'3 \fQ ɑ[eIj2Y?Rð_?~~|bk <?м'$7b=&5.hj h*Bl:bf-^%CBK*KA J/VuصBj8TRqJ4ZR} O|ڽBi`LjfvO),Z%0Q7eNRߐSdKRK$^˚+!QiWV PsÔ9KNb4CwZ;K*y(ٰ[2VɔjrljI@A^z=Ę f҈J_(Kh#0,+e}Ey#Kf&D4`n۸^p\$ /4c|̇=p3/F H3}_zLlV~Zƴ? چi4st_ÙaMX0XR*9!!sm! DS͚5Le7,4c+XaTӂX~L'm"׶7.H4gb1~zX.̧ji*leNnb {f`07\$~ b0)~>-yܒSp݌*۟B%`7|_=PENmJAmHlvwŻD~ϗMHYEʪGDzm{&3%DNxZ%6lek=3y.G,>\afH Y$J?/5 42P?mx]6']Ep@p+tL i$MHRZhjfa[Mˀ(L{ DDQB&k( # yr8"%B= )frKR's_8Pq`zN=fɠ.._Zm: HenAdbdrlс%wiLNw79cTZ(lBy9f%_>Ŭik/Z{O/wј;2syI*BeA{O3uPU\P]%i1N|j5 B{Z]Pk$ m}U9 xJ]@P CFl"8D-'*MmifpȪyVL!iTqrI) ?Gore32JpFwANmUm iMߗ˅YXEJM ߦ?H"ϙUA⁡ baLJU,C뫈\{nquWFuc`DwH~Ͻ;zn,ߦw}O*i!x&20ͬ)iY5v=GqØ 47HV~<#3>\KE:Y";0 LqY"Tp,1~<Śe kQ\'Th_1 z 9UplN>@gHL/8gA_hTcc wuqݕf 9AOx ݧ"Ob7QP I^-x)<Hh EZv#~2|[O L syO8% V%t PM6]8`f+m[׽E1m FpX$(.Zea%(rCP-"4UQ(ޔakfV@} ~έQ`"RaXiޏkQT"s[1$:q;7#FTQx+qڲ[dz| { Js|}hՑmT`78ԕ.hQz^r vC Xua!(B#4nPM6#A6T՗`{1W*vhJzLjyS j fTW1Qwe ăgG?N((60}ϸκ@3W S@tߤw#_ihIB8p%`uqz80nȤ'Yf%q$C sDDXE/a }+*6RQo[؊82d4D0m;D oVWx0-[ѵydS^?ыꡜs׫D-QpTT߲jsG@JؚdWa&zdk Ct̆T(7{Zsl o1b9*X鍹USt`4q%|LْzqWJפ kc VFx 2zV#B+"Ĩ5ao^֝D E LyoUv;A}Li=e+Z^2Mը|U&cs=x bEN,r?ugMˌSZ@ַ?9_ܠiJ$wfڑ+Ƹmа"V/ޛ*3jpwW]ocsm`ɫ(q[ 2b+r-UNBa}˫X^ O{f^NKN˦Wij+HϬU---f$WkWE`ty_gU%XU#u0="[]a6eFrw(y:a@Oov̅dƪ@+LpW~4R@ viYpAoנ#%)*9P~a xB?*MYt1яlߥb$Tg "A2$ kK2bhLݙGMya޼& [&݈fյwnQFtr}hDY3$I%ٮ,&D]s} w? z<%qr9,#DOdž0 o8-v hOE3WE{bcb8s.`EN&>"9"wUTs RO 餦11p# (h=2P.gp̯)2u%@2_OV#" dW2+XfQN-T*搶Ny|\o[)ni^rLvᲤ<69B' Ld&[,q:Hj)qf:Qoh9-M43p=ߨ)8 @eQ7傧Ga>xT;ep sJY(7'I^i]VDdZjK { c(>1hQ1J0=.''9Z2'đ%{j>I$@ǔ"&yf5` @jtY`ɀ8'_͆ugn?R*$bLpTO ~xf!Kor pv+Ci c^]h z,0`=!0Ic4;I-7,2w~(Yf_>d<J2Mai)5YS:2c=ngX0 Y~vqǞ')F$TƖ|s2?V9(PFٴa@V; aT$k0]X# c~̳:gFC$͠9^NNўՁf+Od*8eS,`3^TKKj!Ssi hct G gJS⹔ǰ ҆ƒꈁSA(E6u,lMN(zF#kw-~m& d؜[bFRvB ʚgZ=pL_΀3} Kѵ?b]^KԒ8#& !y6Gm*脘 )C@| &]r1n|<D;8Lx0wO_roD?=?T.5=}Jq;4gZ[ %UcwpaI:32jGxy'Oߡ ٠kk#ş0yC|b_aߎ'ҝ`I<隔w,ŦP'@E_o GAak@o@W#aȫ{,E 3w1|͕j "rg S")pL˭vJkTMbE{Q=N wX{GĈH@ʘnAQ{P&=5HZq۹A[y.AAx-4 ۹Mzx=,j`o m~YL yZֵq!Mz7;5NJ̳*)ߪp&ZJpU6JNUCFzh1V'MZtTٻO⌰]l+ktP`.l,h0%1 HlBWX8j4 -`G$]8 j!u?|_G,>e `fIN&4DF2Agq1?w`AO-(MNhtsb~.J2Us9 XbsV-%+b!=sz I!qTeZbt歈B/H|6u=kfw3Urm^?sVZDL{p:.&XCK ]1G:گ)@ ~ɇE7ȞY;Cg7?0"$QG蛆 cOPeP 7of;XpAzWl_o,?[8~OnNt?;$9ǟB{UXOJD-KYdo-JXcǖ2~ӓ-gð?l[r@A›2QPV=(vb~/s?{.\B>X0†:VUhw3dC!:/˙ɗ # puWh-ͬ&ƊsJC>dT(yN2rԫtH\W땯FM\3NI蒠W~2dw`[ӲPȀHʒM%cElh41^˩bk/ʠHXOq;eřjD{sE;6MeaۿǗ6_V ´O3M3-j$:J,~Z2Bqרfm/,qL37=s}BNlJy |DŽP#PaDA^ Sq )OJA!'}YT!/  B}@g-^bˊ9H-廃!Ծb*:Wmʢ2 ޾+V 固=杚M?Kޤn؉*:b"$ոY_B4TM$bw->z+ySEqfvSRM!-w|Auc+A>SwF)W~*#g G;)se,hn*Vo-Gh_ۊ:%Pxa 6Np f' ҄oO]2z>h kĶr:q%ܖ˚GYP/) 3tP&@NjaX$i(DZ"DoLۓaLEXfALURryc{ aAWÀ?NHMwezЈ<Ɵi=2!ht2`~EH\ju\αJĻvE=&70gOd-|Du]# yܽ3Kx,U"wRva07U%qZk&<ɤx˚A Zml4"]wSjBF},bg))/! ~N'Z-&WHܒ0MޚeXPK*DK/zlz Y `.NaBEt;l}z8ϰb:jhZgWD1+'bK4ŵ܀橮%Ke,IT*[2r7ÒG,V3Q` UWߡND^+¢o4Fm9ŸuWزnh~?+fTcfZ&@/yMNy8N%:>RKd$.!/dAy]Uߗ~#ǔ""Idh|7-3 JpAcns>g[|8DIq5vPn* ܀s,i4Ao|;,=bA 1@eٰDeq@m/wb*HLZA;)BkQ[*A"8HBB#'uI}NL7a%PA ۱21]>VD*׼WxZf^qv+2b*3)L{ɕ/638S,ݰV,k.R 4 A޺$@QK;$I^1\56:#ŶMdѹ9T[yNݫR剻AZGF않TFN5VΚ)gť9 >ADW~C @lͥɨن 68.3V u?swߖ4wA)S&j6C&cRQ{y|앤C"m<}tCqK* mkxj ayЀI˽Etd,?ZɞuGYw3\N(bJ.{RPBrnZk v!m3sYt,FTq;3dKx\[i|kwPr/f$Tf#&>馍]r =`/z2X0E`dfrς:}.v2Ƙ,B] KҀ*mD[dA0{aml0јbݰ 6@U8%EFl&mci"I&HA^ oO:Tnk.`E(s(,C%&ޒQqyYҊ, Nȝvk$߿-5QَHv%I"g#^;Cx7`nłU,nɢ+MB~J^g]* N%j*,})韞Q^KƔG̘'=NDq]v8/%0'P{SC(#/l<'ubNGz6SxegP3Ys5l0Hz6jZr퓓`G-PKxCykMg7F9uȊ0Tk|eèǴ&t,3.昵NOLt~f Ɯs9ln"\&AO)o^EUZ` .6nO;{uE$yGVNDM9>s;lםYxI6|h秼>f"a03n7q;Y҆gifԃ{rKfYԦuϖyE]v#j-żDZ寤Q6?"si'nRa쳜<;3h(%R%o?#nw] v?+I5̶K:&?Ky&cSni!784󔃢f;A[؎ *aX zdY`V;! *"'F̽Ʀ;>N]nV FԢos ]afPџ)K;7հ& H8U@-l$h3}TrLs@ꪩ ,i~Fnᶣ^\,} -+pNRTp1du'nϲCYǴ@[eߛJdWQnx3٫dv6dUAUq-ӓEe]Ywcۣ oɀ)cTEWK̨*F @J3a.WO".mk?1H*֓X=e?0JYn3]Nܽ { d?6Ƿ/:O^ޖ?(h; -v?_dVY^EvWR/u2S^\AJ$چoCGcp]o|~`<2^֮4DTWu]1Z`hXFD|t#v@V>W]kA:lh?ꙇ4#ns+J^Nm?!>~rPp`jEu.BBavx95Tݎ: m7Î({k!pحƦobf哅Yt\_xa뙝hIR؋ Ggk]7Y%6[ΈP<"Lr-C[uXlI]jUs%mX~t+y|Er6?|}26rE"#ibk}n$\|-Ⱦ4v3%,%ԮKR-e 2h.'yβg8I֐(4TN8pI愘aO{ZUI6-(,7LC(Wǎgc3QyF"aIS rw%# {>OÔTi&qX ,4+@|^*׌܏'Gal: ۉx?m}??=P)29 [)} _JSE2՝ҵk$,Ey' e.44/K*b r5t3%K.\[B!#~|v? t/H o=&FtjƢ[56<:6fZ4٭!v^~sQ Wn5 x01TGԜ>(K Nr)@JTb.M"&pj, 鰪يM7%l} wYs\QG 6$IEuu碃YY% b j/Q"c>zNk}d>LQ U7F?ayVF,g$;s'U~ڑ!f+4qq[lqd*͋Y/L }DYEFr}]=XGz.cʾWv5ƻIFf0f$ݷ`i}KD!J$lh7\r\Šv5°7X3: E]I=9sXHjOWW_Do.P$ńI학TI@#~lۮ[Ǵ/yt7A$`F/Lßi+bk<0:6&y.gIʧh6Zl;~;/q qc)*'ٙVCfb|QE&4k2q- S2%h@`= xR"6IŁ  C]WrPe4wLǪ  JcFnnqRZqr;ں02Xg6֔Dęb4-oS0!G=Sd+o˿_Le?q9rXB&׶]BK'"D. LC E7e}CASW%pLA9Z<>!$^r<,46$̶Yx/*8Q 8CIfV`&.v 7M152SPc 8@./xlzq l$fn=F^mfōز3JvqNa+ _¦%;9|- .~8"<٪;-z %)x7/%ETf?apiSɭRezHEdE V.1OPWUwGNz HCWN4$K^"FkKwdC9FɖY{mD$ዽ+>"FG?iaif 7=OnMil6nugHG9\M߇2Ii>5g 4kƦY e:ņ HH/Q:3tha;;[[$\TuNm\4x£[+ 춽WceW`1cgZn iѷ#q]1{xq%|U F7%e ,[@24^P&ZHNBe ] Q՟`GyGyGyGqO?˟~OEO<Ǯ=a)Y腎bf1TLw=6q  mQ& P1]0G@{=5Uzee#ld}:AC[a`K(c%9OY*NUͽ%OD<"Zla`u:tn \DHop:- =Vn5KZY e; $yP1i96}7sHeW_ñ4!1LP!`&Bm>׶e3=v[QZ4͏AE@1jXB9kFb1WUb)Mφa|L[5x,Yis{Yz~߀%JMc;X78dŧuF,f~^]rH6Ec{ެZk`%?x>4a~q Z7<+n@'] ڨԖ  Y^l x}H.8Wg@Y_IV;(Y2C vȔYe]}`$RQKADǪ,`U&$c~ Bx;? łKo5@IKrs&B  cpNJ/Z`~0P-[P\<$K\_}/9"-6yi`1:z!߫9OFs*yFRjԯv)빛h^ɣ%$F/DX{Z,K=SVr_%wΘf"Y8&q.kpy:.4VV;s U{ ɪO+_,Uݰh< 7"aOZe#BsR5Ǝ*X4mFNxlbИM2B>^k BZ(+Vs s PkH)p9K9LiwāpVE])"Z @6$8ңzҤV+By/ &=`mR$Di D=BNb59An^L HG>ό85n8h< 50.>c4d=yc@nNHꪬI%f]Fj6-;\SlE^c$}<%ϜI6yiOm:W(6[4N>P? x*eQ8=PY:΁?y,s!]qNkZ$u%Υ."Xg]č%>?a2f:~%h!_5˪v\]eJ\% +Fv:,ܭ g@k6Ūb\!ǐK x$me,<1!E2dc\zmfQiV9914[Uފ"tW+bs=a9Yxy5DBY"5s+XYx`.9z2DXA,4fKhFh@ӝ z1>p]e #uc(k?'ޤ0ֹt=EhH2<Y:Y1;żyVͲ]Lt\z:԰zW P$}WRi[ sb#EgvYyel:6gBUdH|F-CZoojmŵlM^F0^Ks)a)f:^Mj_Yme\n >"HSk Ϣ <v(=}KW\6,Ă+mvcVǺgא !gcVV0F *B$SO!N܍s%!%?EQI_0H2Iwy/xF$!,\L&!{ߵ}&&-Ρ\0GuYQ)]ܱx;f{T?놦i/ YlБeBq_IšF ^orP5Zj4*M:.?i7uCASDRzJ CCuʭ;U;!ű{&s1>$A\5 d<˲Q&X<]rD7GCt#][T?Lnz[пeAQB8PS_,v*w cޚC<}yS<v(g5Bnsw`Dе {T7}b_ ϞpS3]geQոX^`]G-=(^qϖ/$MxB!ŭ0ZNFh{3Ķj'= ~ژgWx*ٜD[Ԉ`@:,._ҲY?l\{]{P$q!M4ʎedx_e^N*~'%,^׻6'ѽMVF:W~bm4(ih+L6Z=+ц#bڢ׼oCGBa!{s/|PpL f;`'5J<8= @YK.].908E| w }n`Q1)>{~9I3hZ]Q3世U|H-Jhe ҊrrCsɯ%ߍ"DU(*AH22X` |XI o4S$12CSm` @)I?AsAU8-d{*Yrd`{bJۯbq,IWGk,Z'aAMy Ҕjj4Ykj%|Z;lu@9qJS*GJBx:P/1-pG;jYI&oT/cw֩rz!Q}r{vp*m"!c~&|[YKo"̓xgymR:J\Z3b[ǂ(2t'OXi?Dx/O^)μ>Z~ii⏔w;J.pOk%3$R URGq$)%ܮ@-j)cM{(vi%^̀Ƨb(ũ9Ky˪cZ18e]TSq<iy@nT(MtM5Gp{ѵmU- BB?n^SqP :!v%) c?&!Aa@èbʑլ:* , q׮vKgOkl$ñ硟iXMHw OdC?#(Ii-10qO3Uԭ~?~֌:snDKO?s 'i/Uim;?ڑEO$/~?\~r}׏mj珟bMrs=i,wKR =+NjcsF,_E5} Iv`%{> o'2t3C$ۜv8GOG5OC:uʹo"t{Y f[s c9·tWWi@v2|ݱ;8/ Ot ,d#kM%-;o췪UzNSgl-¥68E¥)fBa9t37t3-0Do=O}={ޓDlvhA_P͵YhBqY?}w"O&mJ`w>|tڒG `pѼ"1O9p 5ڋ gj]K/ ͏LkvD}G. ҩRGHS`$F>T_t2yf >v>wЭ螮3O_qf0h2>l?sٛ#N i퀧?c_$mcrH5Eh%1`Zb^b0i",ٍX1cm=-r ]%Ya)%tϢ: L@ K^G=U 8kQLnfQ}}H['/`AGX\C>,lmg4{YD֜ǫ2h['5/~nD,Fu,<(P[Fjۼ7q-yhBy1/)5BIA6x>0⑃k!xT1UWh$J(u]#H&b A i~b #)xɎ>bpaD) ?bAܯ_ЦQ q5$)br*a_۵% ȿb8? &;FKRhܿeR~20ᣅ@WW0m:S74Z4H#j`0fHĤvE/A`0ʾc ,0Y& ULoAY )Oyz6?Ce']HI`읈.UdUzE%5x"IDmȯe 19Ux8)Y7 V4IJ07^T|LtI@W2[8'|[hIZb vx\MlbHV(!-0Oj$iN*!\EeL2yzƌg3BZERkm[䙀_9~4 h=V;lYBkWVB.aD Kc\ԼtBIub~gT%;gL4\Xb}Ѥ˻0V*2oq@ !Ƞ<i5K%o*io6X8nPyc!I'IFa͹#-"TGs<'TLuĮŽյ pfHL}*h=G[/IgW`] LMC@1ʩw཯3ra& 12D08Zn$z-wId9{Z^WM5J3M35q^`1> Rڹ#a2\DN7Fb\5hd4C6Ar2;WZvZU3\fN s41P1G&劭i=^?e>Cfgj)tSҊ| L拃z]m b-pȡXGCVkڥW͚ JǰA)Vh\N"ʀJ0*O|E߲iZeKd o*$n/{Plʆc[hYanCyC|Jbɚf朾]Ťb Uܤ%rdZ8e:M!*>ųրU{%[QQPWL|Rrd#j'Cp9-PCj4eaxzC| L(Դ?k M'gP? B]PXXiw"PI<4F<6G F/-x™mAtތ B 'f "]Ah\Y#*FD$J,҉G֜~ ؾV\_ I愈xk+YŢ9HGiODw7 KF%aJ# CM12-zX.)?6"1,U^( >H'8 ډp1q>x[@:CUe1l0rE|;~i1`B]ѰL&f=V.7J[̰yu(n]LeeHd=z+IcݘvozwoP ȉGa<Q}\B2S?[|8is\F>ܝ[ijw?>o eG! !نѐdIO%MF[KQTPp1Xc2#׶.s~x[ G CTGk,ȓ;ԡs?| vBȩ _&6.Qm~s kDIe*c3},e$.鰠d<ц~v"G!pJ)Pߗ~רKgP# KoK#չI] o]e($RY #6%nc6q<ٵ;H `BEsIYfF"BՌY I`3HA̲R{FВohg*vagW$PRagX⌗\n>NDB&|3mgJ?(I `tV,V΄F7r A؝ 1e`c_K-͐Tw= 9:$F~Ӛz 4 j.FC;OS޻z=#Jr&ΐ;1'LE*,훩o@Ԫw0ȏf #Qݫ*r*F~\qD[ j>rH:K!oFUXd}ǦnEz`d~Q߱SSוyUtk1A":Ѫ8Aqf~9zDDYRL. ڤ@%dn`ׄ cPThm ̘M6zZz*/*!i'ǭDH< V8 ?/w{"iOϏBo>NDŽwP*&>=#BQt" $z VtY]i%n'B)Bۙ=Itv[`cqv+5f5^0N6.S&ЈcTͅEXP"Z_4W=O? H \ mr }"?Sأ]Gh3bzpy<~M7MF;n;oP`sZTunBeál ʤD^?yiShbyMaf_JT0™:B}u]7;NP*"&W/*09\X~.on1f&:Qxo}a(V8pݾ W1޿a@%f?YvN.XN(QM$P:nXkVnѶU8k^ d袇|}GY.C~ 4f2$͸6GTϊz1`q D3fOn!DŽHpԯYv͟#wQc`Uѩg +>*^[FH% bK2ҥo6',n1-=?4=XM 1!<`Hh*N@4AW¨bpY{6w11g"v~ L'2iGAG˽C {)Q{ezT~ТCYra'1d\ \Jkׅ付.!LeSdN]$:Y}󷜦e92Z'BItm!/-ғ~:Y u)_eW_傹{7Y1=߂ Ƕ^K2+54sZ?b:UNHBwmN0LVaLAftQ![7Y^>[ 9jw HZWWd}N%:el&sMruvNk02,@J`%) 舽pdNȬ?P Y. \6b9OX2) ~;T@jN" 4J-~/"VLQwmD`K£/z.~^ "E>m5">p $y;3[|RסSbWI "m>Vq_TqXC 'kjZ4z3|i$1#vȽmN\ZZڽ б@{)"?U20)^%`.o0u:8WEE&ɲ[+Dd?Vݲ謏#cJiR>, 7je%bapz pľ&IKKEvI7U5Bq $jwHdě1 d|%bɤg]l1%]"=alxci{qʉd+JB(tPc SM;T%~ ,VܹYc]/]ZbklK{E_hfh0z\U*mC$iua0XE zaTIqUPvx%K'] ĔM1u3bWϑG+' -r# rN{<1žA/{ɠeRF19a[VL8 KCN+Ry""gwEdR>6D̵]!$Qu aB~,ܧr Cc.nQ/O+F#ĵ$ǵ8(}*+5bl 4Mp+} MN?UC9I?S[Nr88/'5c!d$p44Lsգy e\C3frۋ˩[i4 QjޔҹsqP !apt`}SůdJcLRDV_>WpԸBJD`}0F7w{UK,O RnWHfv^ؕx?xOnу|izx|iBC/O%S.HwM/.0 `0PJ.Ѭ ZavG'.zxKp'{LƥDW&וϫ5NδTojj]I1[kGȜ)Y)*vREĆnw#"ZMTLi4TC{3b^xweϾj=9홳2X{ %`0cF^<}d @LlvH`de,1FbmsS\2.ʌ0)c N5,YqNWlwJLwwUP20E/X 9`ᦎćpv)m.vۻo:h񶠋ɈZ&V& ML/(|DHq5Ĵ 77* ]3k5,Zrx>I.ˉX ]KKgdչ>>ug9Ee䄚V`YYa,I " 1ŕ]˺qlWj%'Чx.."eP8idԜ(nmw7(Aۘ[eC ٩E|Q咭Sle3`PR8̨;P]:|Vlxž䙰V] zܜWO{ kW2HSD+ou $cӄk`+)ɰ&*m+f 8ips̹͖h Y+F! uЭ0Zbo 9-Mu';Z(7lo8aIP:\ǧ|/}+gXs~|Jp<5jw%]8!ig3M\f.F)a@gVԢB &fgAp c`R`ݧ! G4r>KI#.; k4.p*| JӟhB1 &vif)437oqF@UKgqkӬaMŲXB+CQ:9n;IqIw^{ U\v XoǏL=lss?򣤚%(=344 édWX -:2+,d&b=;Gx_3E'IꉡrHo!Dpu&eSgFMJ,`%keʎ{ÂVjX< FB']2r/~heqzH!` !(Drʄ5(oze]^PN>rhBvhU uʄx jP^Bi?hoM;Cb߾;(y_7߳=?2͐ƃ` J蛔GLNqviJ'-Ari1N- }J@cD i٨&!#i$ޡ"=if󟈈ʐe- DkuB1tI 1Y!h3[65sD}z"'3>#&i`S Ev#H5DgT3 {(l%Vbw]{ h6'kŜ-W |1 |_ORN`X\҃764WDߨF0xƶTj{qQO}m+pk~KVT2-c95>$wawEcwߛ0@d''XdZ{ ~T*{'eZWr # q;4ɡ=N(Xv)ƕ@kDv[S˜A,6FS9ijh+$;ZQ; et쫧XMvjq>Jц#âd̃A8"g HfXHUEA OLðYmQW_nVc~@$Ti(lVXJ7rVʡ$WOiU`^?z9d)-\ƐIOz=텀tc@N(U99NF2T\VL|]6N|ʥTMQcY!EttـCZk|Ub^G4֧ QBX~UxXt:-eNlS}\wbH ל20yW3ьaO@()m_4@ D¶. 8%ҸXو@L!\r:l?@F7~Wj!T(pr"]qUJ aM>ha,\Y)I~-8OG{cޭZj%nn*D=kx%J,Ft$fƲ7L1OO7*f7IwX?3XտHpx;c9yȖc"O0(7*7TЗͮqt$GvYcOIJF uk6S3&FT`95mmmePuZ 2\sXxn7B1[] үK`E᮰=D>hcYүOpF ]uRUIK"7ȋ JVT ~i%b<%]ԵYtwcT4c2C.L] z)hY6.T?>0a33ޥ#] kd/e|OzSd&o# [)!x/=a7Gր'\)eX9p\6|=0pw+,40Wj6"v9Ɗ'tRUK Vj X6^-e˺=pTصbX%c'6GUa_bzk8f^Brm=!#mh%6v/%x/mOG@nꗚc{8 )  ~)~ύ$ш5IBEpEt8tnl?)Yi/ޭ%'ljWݙ/|#>|V,jE oyk<_i@0}޻t霄vQjڭSSbnJk`P~5Wgϼzv? qӉ`z>S9U9cNضS)p"yy틮iݛBǺ_oj*+i{z Y\ÏBLnYSW iQ%آ|FPGQ5fV$v'~}cV EV736rJ;_WxK9`Y~fe &b52oՏҶ$Kk9P *J2I?aKSɴZ?:I1eץ%uŮHro]T;3U4QOv.[g~-ݢI[/ݲZV ԰m_ʷ+ɩ/z@WpSux/>rPpSwdf ^Yo:x{9!l[]/8yZabJ'<kp;2 cxKjmeM-WոesjÃ8}ӳABrlVE25BC"=7Z&3QiiJ»vڔִ <&yt*4_1jW>t/I{BIa;.|NR9%^p3-%w ,jn=: ω+%A2G)f'nh%\4o]a;aVuh|\ohV&T-#  {hE=")8/,S]c5X{iW:Јi5>@jmf|ǀP\^aioBtg;5Kz0`2/<bv}GmqJRws[{ٌR&$3], KAy]7 ZmFj-n6y Hxׯ@ގ?!wN>l ͱ—2uC8 MۓvA%GKg;дU@!eLT`!6c{Hq! c؞DIM,SxQ:rSi 񠈭PXĺDEWt] K7M~Nk,b鴝%-'!Tc>J q |ýJ,-;uoZ)w սF."l8bؼֈfND]70܆k{?f繼K]ZlYQ>[eelůjCtXSJ{I1vDh$bIs,`kP$ h6 uF_Z=6zPCU\iZ;eOqfk D ee[C@C@UHqll1C W6 & XذC|ǂIW57"'(XA}b.O㷍_~(_ʑ{$a-@T[q=/W#ey|Lu^^KL#VGP?pn]zؿj ~ֲ7{ ,/̻߈t?%p[7â1=2dF-9zvޜgx9@wZvhYp7'7m ͐ A+kۘ;#{s-0)8e#aB3m 'Xr5*N9VNx~DZD㲡Æ(rk?%,^"c=GGP"8*Oބ~{j&䑼4 Ʃ]RJ'wc_5$KTdͰ,ޯ؊}=^[=mi[^<.5":jߚ^}vX-_0iK ! Cie<~z zcW@r$g9A_eOɱG Ҳc~I8Qr^A!h6y[KD[ M"AZ|zyAɊ?w =X R m#I.IQLm`j( vZΰ`j2c;J&sJ*|+{{v8pMݚMPyN ñ [j/Tx^1*F ^iO~}CW05AX 04L+BVf4Ijc-8j͓75k tc&ٚ"EQ&u7"1w֫W+bچarFN1$^~5ޫe%zq$1>%x՘'5 f}^6 &KMt[jI0^ !u$ P7Soz}{\nK4sŚX5\`>"Rs ι/3OsN.:M4O(@sVy>zov)f~{aD":"fb؛qHU>'"|f50=,=iO}L8:4gyT#cw&ލt i7/]7;P鏦FO{g.̂ZZSwՑSb4S8i)t?\ӚW7Gvfdʺ#F4Ř]c{X\(-|.R3zR#֋LH$dI])Cr$n#[ Zl*ԞǮVyt^%Y Y)|"蕻YTvP )`rc6,c}CI|`3q ;hɚ9ilDd;\@Wty BƚvSyl4۾l-ݩi}tRԩNka5ԥI7=dã $K<3X讏'yt^UB7̲uXnw{kUv^ _?t%>&71d4Ssgd6uIҎ!tT' _^eSmC+v}S?v"((ۙ/qP`y9~ڀĖejPGw3R 0#ݮc|̺"JJ+vX5AK=a |~[cC Ye=cw 2a+$o#Tg#'!60GnzMI-wҼp @FJW s<=[ '툵'dss2wI}W\xLK0o%+,<>+׃X>,P`w. MhOJ{ la~.k[l.noO)boC{k"0L+s!:8.|`e{'NqWnrqƖ aλJn6Z\zY ̯5Zua/4.'g[:(+<,0PkCtד-Cö aW.&F Xߊi3I"5q+S aCM F%D-mo{ك)_Vr;nCo9BC-H9.>*~fKIL.4Ok֜]m%,񷇉*o/t6XL|80$i DFaZ+W(YqsWӶ_?x(a;yf3~Wv˖ ;Oe4X#a0VƢrVnq=TiK2g?YK@=y(Ρ0hd15^ |VPq??53e ei/eWI*zmT.9thD4 »Yl?O~a"YO|PGZ `!ڄz sAE%OXFG~k)?}` /ywPaUǷܿE쟿?zjAo^A? "CQFGJjD d9IxJK8l\B 2=c۴o]9_ yv~i`I.8u}]Z|EI$8bgykU7 llLyvD%]D9ݎ!nQ9Jy2O\rմ]/*Y쩈j_ {~@p|n 7xy}8,}VmRlхw4]oLyS3[pU4 WJW'`Xq!5hΞY!aoru3>|Ƿҿ?- ͍>Ҝn^Z2:88epO 8|tMkV Sݗ%Drs&EpA?oHź-CN &:UڪfG+jo_ 61l~YhdѤѵin|6C [utPW!ڙ:Bݛ>ox# Mf )`Ҿ.Uz=ђ5{r cVh2EP?B;@RZdGߍG/Ew\]"HXFS y%`T[ęjXx]TYΩ|k7Y3Mv՟_yːT펜$);檽O]sGw>h71mhv%*EBA7/7Iݺ~JR*<sW3M)hvv_7ikxJ" 0bO*W GBgVgqqH"zتB'h=M}?NGM5⡲A&W]C%z~;hYavr|jh: zG6 ֓F\ҝ|3%26ҥ9J*<=v*C ;I@xnԋhnz;*NyX\Į=]nb2et@7#5ǢՐFWӾ4V V܏Hɱn2B}Q}LR,Ɔ2歸I4F3RP ]ouQ %w kZY].`2b~K*'pC4 tbhU[+>1>%|'E40`5$0 Ş<E8@򰍨},Co,$~xn [/hRd8|UhUt7zU{/+|dll썚UE:XQ%Ic0\6p%^tZ|@B!`6?IdI, _&,Y"ITr Zf)MQ,. o9ޛ$o&DZ@\n,9)6 +BxSHK hzmq("G#wk`4):fFoٽa,Ƀ7]r3|2oVp3[fllPbizqPIf9 m>vmq;ȴ Jgӓu$2^c#i|/9&?՗+.80K %c?R4gߕZHfXt4`UeD63# luJjlNa_wɲT7;0|m1z_FU|M=]ci%^1nV"jx"QL}+/beۈ<*{3FӼմ0IS :Ϭid&Im842.q/yiTp k?3if?5>rw3zO;ToM`r<* cK;J4!w8rt`Eo|zVjR6Vf*IP4ݲ0[J%s8C=T]j7ezɈJyIV!6G3*;\jg2<1n<virc cܾtAfvuy< =jba]^"uRjx׹ێ#yO{2<3y%BYUrk@1'3ʨ|ݿ*WƋ0|tE 3 MGY*JvGŃ׷ ۻpb֦y~tTz7]|uacXu)5.%ɝ6IlpPL%1۹nv'e6A0DWaW;PFsǽ4ٌAhďMsTey-K7/u1QWг㧶teTF 6BQf%ށ\3śF c2[U4.K! qIb~SJ1b]9Y}xⓄt9 vqW\_]]A4VcL ǹͫewAo}Z%ixlBn*VY$ܩODܭg|qXvW#rr BޏM`D]jիQ20aB8\у6 k#x+ jίo._)dJKԢQWnu!FlqOҼ&?ghX$LxvE^H&M*4CLHR{bp`Tf'ĞO?>"4)-Z&@H;P5Zb`FXʴuڨfП=___eJ",|zǵ@[4W%˪r&0tXu I/L{N)+ug<;O{$ӷmrș뵈6H);.:?̑$Bޛ+nbET5f?G%\6IA"Tbx=lYT$31IVL}8 ] %<0AI&_#3VpBq*r :K+AÄvxer6(qڍ5 h:  !CzRk4*U4~irE8)g|ԌF.ŽNp}ny§xٟpM;Y%#125"t!VmU* T^ ~`pɭSO ^N{,՗+!A;OMJrWlC\ t^wR9'ι$'Oy1H>DG->V T.Άa dHnם"XW*w ϠpbHH#(aC|\eXPilEc?߬Pi8_ ϣEsɹr, `nfLy{ OT(+f O| 37h 騯FDk+A^ϐN;xqqmx.+V g;KI]ZЌ|I ,IQX;mQ%[8P_w_S-wmwnҌ.̴θ' qI$95=$ymK]Lyo{뮲WpڳQXaz.H(w? l~tb{醅\J;v|u)$-'t!Jd"2&~ƾ[5.nb(VAl[2L-̈́rs,4SmH.4/rhaq[cxg2"[C f9p/Ei0Sr#lfPq1L_waY-WouE,{?q#mx'/yOr'ʷF6[< 'Lˏ:[ r#yžH IZ2DĪ(߅\btnv7(JKS7+Y~gKSгbO t!n(?i#i)7{  ŪguxhXo_/s62G#An,CT)NQm̟'|P}tXn] pgN!$DK(PlIR8N Ꮰ?7}3QpӠ Vi9ᏹP|i*( Z,#pCՏn%Hʛ.xI^HN8zwpc \n:iOW VB;̓`o?4MmWl^#˜('es-tHoLnwq` Tf V!-"tJR!55MϕRsq0Ko|NײB dgl7$-F`g&c[2BsemfKbbu [9˝W /@67Wiœ)t6g ʍ'9ص%'E/GʗKR* հvl@t.z2H+M*e\VH/,!d%k_%j8d4bA%G`LtxC^N/;w.̘-nfUҴ0JR*ɪqb>%0M},Cnq*Xݰd zH6yCY#=^j0giB,\)y ;/ ^cd 4_rc29 $aq<߆j@H9 2owg$40J9Rj#! ZOx3^'ء|n_pcQ.-릕>Kڒͤv9o=SȆ5%Nf,`k]>Qe<[* 6MZ]|eʜB l);ݛOݦ^2Ekvy/~[(s̝}/uSڏ՜OF *DNrOn#+ƊZM <4p wTWӏBnb-$u͵ dBvM /mܘ)^%cՃf}{ٻy3#[FptƬ{.xΌ^xnAਙ8; LJ^9:2p}{˼%)~" ز) (܄ c~V[|pD^!q:{s lL#yaZ;^.sF=_TD?[x{HbyUTN ;;䤑:x Q6 <$ۼw)w{&;jhgme#KM %6\Bw: gIpvm<` ?*vDz.Q]7އB> 40rVr"+BI>vص$sHb=xM}hT{7VordG1cy.f+a>:~W"{(џ7%X.oIW Oj"U}i^˦;}YEcy8a e:!`Fߖ՗w%NB SHë,f4y3G8"=@ns˾Pp5nd^q1[*J!> phBI⡅8ÌQzT҅ K@ccBiy)9Wfv)?@y,Ռ ڎ#D9]C@!SsϐT)p/:[!e 'MҜ4&A+#ݩ6w:$VeyВdb,)Vzj_@.).nfZ㥠EAF+l9#Gj1{g9?0Ił i$p&$~HgC 81XqL#1No- A= 2#xsM2}4TmϷ޶0 O5<"~^i=R2T!ڡ;WafEgt^"Pc EeE[Ϫ?ҜpT)?0RD24RCjUA糺ȲLe s3z,ƐW0ea|0x,H\"j2*Ldy=B?AdC7(n?arV] -MT d [f8=kx>,Gf[u$pTuyA5bk -ruk%sBP̜G⇶˩~"(i'2y.n2ɊLMڌVJKK& LK.Zye}xH= h_Z(.]>f!ZBr캮%8bvx"D=TGUgErbI71@%/0ԅHRJ\؍BQjiL!^31(g.Nq*̞sPZ\ni$ C.Ow q&f+e~s(2ѾrdsLD[Bx4Sg쒪bm0 =TL%4 k8*aB_ Xfj@ߢ2/ ) {EÈuzJ! TŅmн5vaHi-sYxEqF,Ḩnn$ ocZb{,2eh'䥱,v2CMv,9|l#&CcyPCخ_WkWD7#FK<0ĸo&Y+pݚCtdƇй&D, T-Re5 mtUp0ۍwbP8?N3#M7Ah?uzRFV:tb. m&-~a&%. q.LvrpzPHݐ[9װd~XDhD{AŃ$l/<1KgS,`Nz.$tZ&gFPHKLb+z X22]ڰxàm ;jT8eЮ(G8쩫zCikZG޹fʤ]Snr>c2nis|*3Q4C^ENRS[q׉bp -OcPង^'Kpj*b~DEqZw>`WUէ J3yA^EdJS %A^.9@T| rhNǝ,% i,N ]G $v!6t9iDǎM"p@8:Qqnl俘>`[tĘ9AMZQxZ8KBvgbP*%Hb҆JQ( wP!x(z|; mS*j15D@ cR_n{e%)3Qs>ECX{I.Oc{f)gVdڎ5zle8_vݗu :4tt8J㙯3k/V8%JPaf4lP} 'S= ǟ)("Cr*A܇%=A3$ŗӝ[WGa7yoOP$:JP];DmNl5xYzfU$%,KW2& V4- \ܛ (~_'G)Y`\ht.;(FSWUS/.b^.(Scz,)1ەt>O6GjޒT=zϐ[(ZYSy;h3HyrN4Kv( ;'3P^1 K[(۶smКdݰiYl9гWyl|E I9*HـgQD6o.xfmX ʢydhKohk} (pۘP|1t iNRY@ipbi])bJĉԃ}BðG ^/4a|+XԹ A3 4*:]@n%njFtVx al|Z[ꆋFJۣu@_d@95'ťlH׉U:ХˎɌROagltqeY_\E Y {|uX(c"Gd. 97..Ӗi/$06kp%·f_4SJÀD[\o5$)% ape< ܙb78 'x3((ɫFfAdRb&¸rIa1 \r<{B50BE|E-.8 = {àm&لZ&U출jΠK8և \νH"Uws0 '.CXfPchx=Y>5|AFZ(D)]!b A[UP7 P/C,*m=з+^>7[#{CEIW(7HmdzqR.Gߚ2*l&EM5vBt^^paY9cVټuMlϊD1~$\NBf%0 "diʼn,oi4lX:?ٽ +&hȮvMZzijPŅ=H2sA! l]/HN*??sVvb]" u,ٺHwOێ3/N{BܻIT:eCYGOgZ@# qNEFH-c.Ha A^,CY`:N05koo[ќ:0gB5PhP4GDu2 LX( '^ofY&x6^MkK\h&V*KQxw[X NrBiJG΂#}*-Y+V J0{+fZNHy?ybןN.%B;*%!~UÕCv;p(4yVuBrˋ,/2ްVK&hT-ky"geL=+ã }O䣰w!CXI"߆!VoSV]EADgN!kh>e`U䆮TIş 'VpSݽdRBlF 1 pGFZ$)fkĹBMV]Dcl=u413Wr)T0= ئn5f%VSB$[OR%U܅wq"ϢfW0 ?iiI_b_A!AE֘MِXɟKp6(!N :eeZi CF a4h :.BDZwmqWaapEd"JG~1һrI{sܹXLw.:/ q~i6)yls| 8{:~bEV]}W\^%ϵ_7RYfk}i\{=c-PG棍U^X㾥3a)|.6YMrN+Z-A|˨7 !HSruzdž rKI`&0{-L3gN(5 " p&f aӀx; h438OCEhH@WMy;GP9yW#ipw/!u{WODϸ5a_$&JԜa!0K 0w%E}$Hŋ"L{T^{)o%.^KrU:BhEЛ?h -*ua(Ops0:SC̲iT\[(VsVr+(^cn1idN 1#SUE4 fp5OIppLJcԭK|yW|v0fe@LWR4m$QfdnimnClhd?lfvG6*'a>Fy:tYf(>{Mchߏc!*Yj*.y8 C6"zple@scswG/1RAcW|:9}w'y-%VH+u-*T0ֵ.K;>}o1I0"im_!d=74Bv1»50a{kXY;LU`:=vY,a2>k 6y6-2Η|M(6u{-Bk_6N]+m 5x^*חԿ zKLT̥L=C."Hd\g,ΥBp0{QMFwpHݵٺbX8<cd#bDD>Y{L[װ/stlsOrYYkȧ㥳 x(F!~T=; )/LzʌWV K<*J>䋉 @BE "7&z3"\bטK﬒>TPA]Z-λO{-`rX+ϗMQH>9yԏRp"3HN i]D4}s 2T Kh$wJw;XG#pPE>YJ[*%'0NHϻ;WxXـՇ'%J[zDGcy쳑OܲNbu37T}h{i=oja i|E&tb=:YQOC+d=>8G'P[ JݧALخioW Ȭ#gt= !9 Q&P^GO'R‰DѿI #B ɉ{LciA0N Aɇ휼1x?jba^IAM*(1zH*M]dqRZ+l@kcKwAFHK6:7Z ԧc~ӑT> :Ui $ҹu~q׎xaa#@Zxk4Ysngc1& 872iG'z~ =Qô; ǘBXY`rb ޓj{|G)NhҦX/HGȖ>atva޹Z۲e'3$IrȱE >^%jnhLxPeccM6dv< Ӥꄹ`#|~Ӑv켊-9r8PXq4BIy+ i6ے9myg<2,54_ac2#/.GbJ0*on5ufR Љ Sg[#8#apͣ2 ڰslG|ԗ/uU#k?%qð}Vw9|K"33y*#JD$3Z8T=&,RV U>y"|[G߾kb6-tlSZߵ# HcW>e$?Y9q@f j3TMFI}0ty$ \7B8`N fW䲻8=P(C_/cn_bnCwMtHn<85e Et|VL@=@kRА|A^~/5_WRڗ}zYzYR}]/zYIH]MwR{gqQނD}5؝|a-Fhrӝ55H><(15U@YcH7$Wz,YxvN4fH2fRp;#K]Tq#}M>BK4֚.D-NP1V5($_?Yȿɖ/&bNG |rOe&1t½^] ez)jL49q,ĶyHr#G(b wNR jV,C##|#0.BN8=m6Hk$?+E|HQEC[^ƔP׿^/~ \sf:s"/[5#„v#>kئ>-}3*!]Vr8O^;/*WU+kJY?c{KÕUӭjZRlʯ4؞%e:cy?|XC8ƪٶ&ty֡Ͳ}| sV\d XYZ}ɂ߭WJKV9W< a\Ȑ!jL'z.z˯T)\z+8I}jrm,E5*2J#~صIY*d^1NeUy[R.Jm+#UY0S6T1f@RC(L]d\Ti2>߇W=eDqڽvV5/9MU&~|-3Ч8z 3n6za-tuM(y:˟r["ф4PUqs%mg>$NLn,}ҿq`<ti=e$8>+EMX!S3b,37$n ~b&=3u]pe]Mm%FJ#a\2_ 2j~YMB{ٕ8q0R@3X|#=Hb2 Uθ3A&Stڝl $\!rFE*a$6E UVDmxh-h* ٲmOD[}Ȳ%e:rTNqE'(\쀅{~SG=AdE~p|3'~]E\Nw2p|;BM~0YSt ݨ_J<*8 1ڻ|H|6YFT#jšpBD"25˻eaEMN6yUt:]6 hۭ P?M+7cu9i,遀o8Į:ՂT@#L.]>cÍȹa杳2c ̬"ݵǝDLw٩(z>%%=N ٣Gb>!| (m2Td`8G%`~dٌAˇ4toc;@s۱,GH𳡒x4 9H@mqHBrL{G+Qx)T%+ @չ`L@e67sa]A=>NmVXk/}հ0ń eFK434*)\aaYLXKenz-rɾꯎ>‡!bY,`IHgiXA0;ıf#ω/|mF8‚@F$?+ lL>"'2t+kn)ta̙I3uk9RqȠlTytPȫ`BQY3Kb-02STwAAX J4KXj0La$*X2 JJ ߄G~%fliIC|bb #`c A>لhhw?` cYkJ[}|̧ NST}$sD7a:aG/'Gĕj\@[It O'"rzAB7tHq`LFlⵦLSm4U _zhd*{:c"kM*fK:(Ss]2A\y($M*L6Ǚ X嗚1.ٔ2(-H A>+ZBУ)a3o\;Bsj mH0J>m%6ka(]Lݲ u`dV3gr*MBRbҪL~ݲ#+[m&I} ƐIw1A=iznC}F iL63g2^ܠ=+h2?nY2Cl}C nB 3 6#q[ŞŸ-akOu&ݿrZH[eY/}󭘉Le" L]h^ ,fyq߄M5kӦ2!˖˜) Zgݟ`yKGӟt*a]]0^}-,>73tlOn4j#2L`L$"Ǧmt[6Y̗Ilۄk ;:YP iV7jEuTn;_CXԶ̷=KEo Wxp]LA=h]ͷ Iӈ)NC &۶Bj;z3mo1V tLW۝\ynSn@X%ڞ^th ~لq qΫpk ޔ,~sŻD`FWЫ92\Ilu= sGK U7-9wUBnѸvTTmvǭ`CߘE=+ /Y yP]@T[| 2mWY#v}Aiu)4M;X: k!`՜t #=NrtW SZ[ۿCwP[B_K M3T m4R4w2#4{Sъɚ1k@:od`z>\9PI{chd ̿3<- mιA$r}kdGY߷X+yt>m}(Zv~Zh8qik/Z2ۋ9g !}TSf;<vlA7&08mדdi.; wCX\M ۽#}{kTw#bhpoOc\`soxYL>Mܻd-~kͧ}luyۙ z|sۛz됻^flVN;&:kc>|n=o]qt ٟ[ƝA8AoߺsS[lubYpKݢi=݇fS݇mMiՍb})̿9)]8]z_nf'Vnq)w y=nyVt1o!7[[y6Sef 7ϊ۟7_Z$Kݯޚ.fMoG}(_Suo{&;Z0w6{Km(w fa,'ܪNÖ-ljc4eoq{oaAi|s.N\ qf: }?C7gΆǎuFzm'3"sۦCi@nܩ"5˽U#ZJAD,9}xg{N{Y&Btm~+j*AHV6(ޡomN2PAeձhAqy%;i[L@_0)Y?z[5 ۱0 on)D+n¾#<;WD^:rk` od*c{?s~عnʢI~[F}ĬcmgY6 / wb*n:ܱ h,\̉bm1APhT켜'd_b)GԚT#ftD{=;` 6.1L^&Ϯm qFzLRRHt`"{v#[0$Юń*yR$@b1IM!D%xmys+vvLj)/bYaDq l"vMGk\Y)eccwmJ!!Ĵ*Fz%UWU 7(1ijkW嘯FxT(Wp_=[J0Af'oqY^١".8IsTpt v2ǡb@BfJ/Ufe}DD Z/qrM}ϦڪpGbzP=\U=zX!.N;Q[+zs.\WAtUD_- afS<lu6ǡ7ڤ7Fuf"'5mUO 6J8VRi kF7"+\Yx•2(\i3vB۲( .V `.r:~y4kc(]Vxku-J6\[ 5`%s`.H=U`Yse nQmsӒCn1V%` Hly%r}/7q>vr[R(2V\";5_-֔pԛ}6[߾mUNW5H4xړTk>Ҫ7/_aW{U)9^U WSϼŮl67+2]ΤT[$zɷS+ּ>͜*W^n[ yhmy-JË(ڤ nHkF$nIŚPTG^}DOpT5i{.Qn9¨j_e/Z9kEUÂjTT7!)`OTڦ&r9+QXyPVi69Qa&* JޡBE94Wpi 7Za aL#*!v^b W/ھ xŮVQpvbp :2+2e`CFDrQ< 5G_$ 7+zGQEyyQ%y(r©-C.)Z$tA C?feX5"ˉ\% I %"}GuY[YY)<4(7t*7Gkp١4?b,8s. J,HdU8`u<*ކeD"`o96dXl}`DEq.n p:GYN/нwiEt^c=Z_ ~K%#(CFgFLB >c]:4_26ᣯ]q8le\kl@t+RReڮYBP3뛤bn^F3\ s^ͪ3M&x]g(]BhF'ly|Њ\;k uCBC/b_iý ɡn cBk7WybvdUmJL,%zA+WUo%)y0:n9~r \PۤYv+b4]S[Y'cXNH5, ݊lX"{Lok0}Fk9OuE_Ow /|bXp>.iRH?.4I[bXY~0a; Ò4oQ¦LD c.J`ng_|g@Ɔ7Wcks\=c;Nr<31ف׎_STSD 27Ud$˜rq _/ly#.%WlxPMUųJo%> [`\ >Տ *jV9Rp2ǂ]0%峘iJtōF=]pdj){=) >fZ\(KVΰPd-|~RRV?]庁#_6˱h8L]V̷ٛ]6o47#t/]ErNLFM~2MiJ2S'Tm sn^Ŀ^[ gi,DF[ !"8?1x2g`Bi("ʏ[Kl})g ڊjD5_pMT$$A,kú*)0]K Mg{m nm3)#sD-b~k6˔cV6L5W Ql /sMDONȞc&h;%O4ⶃo~ q-*nOA4>ۣuA n[l} t)/iW{js,8_mZ2bG^EUܘ&o*#=%T(|:#.Cwb|iLPeГsl8ɾPOt_Bh J0}'$*PzKD)=, KbaO^8vL:(_z㏟-xSp(kU6 :L縇H\OY54^hcQQ3DGhr 5f(Qޱ qv+1*Cz KՉfi_?3{|TEH=6c憻b1GϐMwFkD9&7/[F|=>3:tE& aZ~'0u^K OeQ")b9nTlf \'$RVP0~'wa RN #Μ1~ Dm롂1,d'Cx){o/̡UxHLI{]9FOⱔ5S00qs[ࡦs= \p\t;f/-f8(t(,b[EpFiDCP9,OݵZ'XXI߄ؒ|0plN*i8G]dAqu~e ֽM@"e渏s AQ<=/Ŧ|yY'yq1o H~z`9]4I!wVK) "nY)ԏ^ގVj2BkRVLb]+m 5&A_R,g/}&aaV@xCk:_Lw<rXkGE!6oXmsX<]715, 2-Rv%D8!{('] %a*+-ۢl ݔ~/,06%8+>L08iep?>MoTˎSpv*VJŤl䁏,& S S.FRȱ¨Mc `>dYǷ*4g3ֿ Ł{P&k|8Xi6j90@Yij(K\{FVp>#&E"LilyQ(x-FXgMtςzG1;Dl\/\[Q Bu'qdUMOUwbOGU;HS~xwжSB)JB]Pݑ@իGsh* ]][`Xv ] @cn,)~]֙];Jm4QV?IW"*L2|))~+wdz C F7`\4I&0/wn.Ȭ/mXi97TtOLd}1Y8=Y!#Zl͈8hh%G/ɛ`P$ZGN1Vu9H)kC6zm!k?5ڦ$7^d8t9I9b9*Nh$bllA89gxSd]w@:FaP;L PK/xɟ%_[RGߞ%6~88;8Zͷ(kg,?u5gٕ 8HJV.EZtSK7U03`"Sq J鰬.Aa~)@3`ԝzV%Jt(1<ʢkU"X/V'%BIpHXa\W1Q)N wySĪcx0\xlչ_5VW] +@|; .r/X2u\Au6865-p'Qy.k=֚Оr2 O"SRe \ú[:wc("EU@EV@Қtkx~~.E,E̔;D:W(z0Hef{r͏1='q?[@gx´ P7buK].EU-oI29ɸt&Ңy:9 *3B)#$Usx VA]T{ErM>Hޚ9fU/ѵ0}tجk;~te۬4YyP=+e'#@Qme4ɴxdQ5i~Пɀ|q})0I9 /(x>1$9/Vz\&-Qn=ERk">+@DK0JGKø.urBt?N%p dhԪ&VXkd["Ыև 9UD4=q5snt8H@f RI^kDǡj.7 pB%; lQBQsaح 84lr "pxںspΝ"}[O5$]i&FvIbI83ƷIΊPtB:Y4I. r1שᮃ)ky&o$>e^НO|Fz 9'3Col?Oq5I(ią~1X݅$ ,5QwpmL\)RXtѳ^_&=I`eض6oLpŸ^C qpG3SJ$#A`l8Ox[>z{lQ @)Nv5 :Vj*K-yIs0C'V(:,q3o>،eo+}-irŖ!J=)xpF "_/-)jA  1ƶdcw%KC=@Zdn/@_ ˤФby6?3A{Tps{#iR1+퍴iS$yWvmXxV5NK ցLf)`^qu戄H 츬u<kR#Yd pr 53Px&QnʻUΗI:aՠ}0k f|Ar;L(r_TKCchIמ'ƆyZKD֮J ["Bv/EI**x@-U48 l I".blV̇mݓ9mK. mڈ4 [wig`fyKu2Tm9x`y._tuטCTڏdPѵ!զrNVc]wϊgp.Ȅ m.ET e\J9?NWh/o5W܏ ŒG ox mU8dem,s>JMVcHBvxk;fCy_ϯy ]QygFtR:p-^g^ІFc46ʠLIUqfٕ duUg +A"ÅB;=[WY"ۋN"ҊY"9g >)ƍ8r/d}E>9sC|8> ^^Lj¨y/4'z~fM_W(C tut9<3_f.J]"bס>p%C[om${ _e Ap{~wpz (J4(EƖ1. ]ðr%L(]4\xքe ܽ%!m*hϗaoS5 | g`y#@Lb@nVQlt4Qωl:/SonI"b>wg"au(KGT__ı_NJ2oy҇,;o\(`^>yQF"CSMɕ%f&K{3~\|Uxv‹ʆ}p/ *h)_}@tܧ2rNu2q˭ &MĘ6*P[xj'!FUFN}84ηmgW7&.!a'ӥP;@0 h\|)ulҽH \ Zۻǽ 6P&RYե" -~J"74BDhV>S7ʷװ5yy[I:|"=@g%S/tčE'Z8,WK3Vf2JlqR^u=X*`ȭ8l@O2zq`C7rd툦~wS.Yf^S4ᩑ<_;)5I7QNqм'_9~=hHoXkk8F;Hz*#:@}5A,pgCǭ;!!S<|,?^: ?(ҿUOHf~[ J2mNjU(>$} )$&N'%]rȖGmÒ?Ks.-H@HY+D)9Wnyu 6}0Ca{y?(HQ8OB "t00lʔO pj[أg}5~Th=/!x", H̤\Ëx34'H <@\wOd!FʼnjI0&}zф5섺S$_T0&:GII#] rv%o^j+*p?92[}o)dY[V\Bp*,nQK u0Tơ/ DB#V*0ݨ+ r^2cf1}"ۍMRQDؽ,Q`玲?9  巿$LQ?Z~]4c"}؟kʡq8U|0)K6I ?66RQl {]\:מX!K*r*MS_-C`6BAK bgkB=gOt DZ9* `߭(# u^o{9}{6ӫp3Bƛ e9^ HTr\)t[w0˸:>r[VOޙ9(`,F1 JJj`(khѢ<'6x).R7F_RɺA'ܥRHy٩$pji%ꙑ8`@2ii1L~8\gw(b"MJ|[)’R!Md1<.]WN 6CN|%%eK\&X[weoUXΐ&d^83|PY`/tv2uvLQW;WףiqXxű%b-l_94 =n?!6UA:uXr CrpڋYvDu`t`+1a!B3b#<|k(ʴ=0!-+Wd,+] Mo$ȩ0Lp )yIoz$\@s#JK̠tmp\~(9 (P%[hyʻڬtZT t0Uj)8 bMaM}TJ] T'gIuĸw8HNL*gT>焬0M[U-L\ySf;S̵v('f0|vV #X|io.կM-r<ر06x3?-+jf;eW)%V> Д5:'uȋBםg7PVR>~B0 A@ٔ7/wjQoW#ty̭3~t"xjneMjuaVJ)f:<ٰ3%.Vy k`H)+5ο Ayԯ+'W2lZ4^ߣB.C.IWn=$$UR"qZ2 m|Y:ؿ7x7Y^a(op[H>sb̷U#!*vGX'%WI8yٌK4 :L'ZW--SLYQDZ=dĄ>9B#5q}2Ć`l( 'f WX?搆w0CKrh] W *ѕ80Z>=|p}eנ \}sF"kg3'\~-߶ta_p)5ADC<Uĸ Ťu(J^M;bo$+%2}L%%> (C`941يB *YKU=W*! a^&U?,*8gT>]F#)brDr}=׋'ও>B}r\^.2SY2 ˠbڼEPnT  K|l (杉 ;D P6T~/ٿ K&| -.2 !k(v(?Irk>NR%|@)OjՑO 51oN1zzIAҗfO1p1yi/N'#6GsrLcR( #XMC4PFa?$<\ DU/La@{ɄȊ! LzcAP\~0ބD$ue2}bBF2;bq 崣Gr쳳/;@tr<;86„[NhIThBJYX(&١vǸ l×}Yk{PjR[ Tk*+8dv_ f*D<52؜%Q Txf7 RPɞ7+c&{,a47Տԭq%ncH,h|C%h8J =KO 9tnqDb) |+`w*-oOoNc?))6$_(1,#)a:A135u )9lzV, &W`h 6GSy0Kx xȥzrWlMn(R;zdҐZV:)yfAKֆDH +U0B,"㟒l|(+x] vl H%Bj*p$Ҕ-|To/چʪC9G"ѧ y@ $cjb~ȁa')<G+Ek#g%5SqITEq;DZR% x)i-i)!f@iڱX86cKQ%uȟ0Ox1  /^fbƜ"Q] >Xwh 51l}4}𚣲7iAp5J|||WB ƸZnӀcT&sa(y?ts1QGjA$+M~0— Efw8@h@&n~ו)̑(󗫛%ssj3].Sbi oasȪFyiņ9QӆxJ}eQ("tA ʙ}L@6)7R7Nz'Ï޵f;~cU ՛IK™`,y zn=8f[|LqOݏ8'g{}peȐqrXxQo1h7I{4O4oQA*`j>OG,wx 1IB4J@̫uQu݀*: Ź8Ka?V| *h!Ǟt, =JP#t2xF6f{c0!<4bJKgNXqUaY,0* ˿<9.Itx;]|jl+U:p&<`D;{,3='^$Ho YNkD2*s`.~0~pצ@&4ei PCҝ^ezRm90Hdwh50)Od3CFIѨoK\.f.npI遞M.HǛɋZ HP0Ċϗjm&"'lQ+)sN- <:{y*H'T !,kEm 2/34Mq3i* O¤42~-i@ {= (›$5l=I 挛b㬈*lr7Ti./r vuPUAP]bs(Z?m(ƊEFqMql9lٝNCW.+'Ɇ~/ZO c̟C8AtQ)] Te֋ȍΘid_u%'JEWSL4\,K09+Np1%)Q;uZ(h2/[W9%wow2׏KQ3r.4 ס(Tk*l}-<YJSjf،;ġdҽ5R0cY%j{/ګPU]|XEbj`"iͨ.rD=Tڕr%X}KakCK,O؍Q+Rezx aqb#'S0*KB%!Q;YV_f ]+i@"igB2AaE Z$uqSnME`$R/yV9iѳ)vKM%RJJ2<4  )Nʎ 'Jآrd?̡EK!`z}+k(3k.)Z +CyA^>`'L7ar) K@5%#ܷ8^f' 2}kXȻq By/ѣC{Q&;אAf zӻF_ꄯ L~K.1rc.s *0$> Ja ca/R))%.Sg^CV>Cj  `~7BiڙTIkwNTo8/Jnήt˟&FƚUŃl _æD+4S|&M`mtl"eG&,V\ryfٲ(XO"tU6&yRbfGCԢ\j@_2,n*]b;δ b>sEY=%^ԝ-0AM q'%FЮ[A3aŸĤ] YnQ,v¶g7(,朎$&'iOq9ף9&cėƄ~WJ H´?fDT8Bqx FLAdͥ9k >2]ny}o!~29e#Hbn#>H&{ùuk8͜R"j~2T@dxw14M YSя0t^Tߏ0sģUAca!«9R5kt"nK3M%Й6^Š`]Y|q nu}[?ivP~bi`\=XJ^P%С[ه zYq!sp' Jc|ᢣ d]teKx4u3x<cp( ҏ?g&̰$T lNG?pUm:2u'd^ߥ_Q#SB`4OKѦ8h dQ6%3&3^~œ_q']td&1?˒n.Yur C k.Dr/1(2#n&s>6.u_ 3W\E)yPQJi)).+J* ˇ ]zq%C/Y?\w̔xyv|n?W4؍;qpyl'{Ya 2=3fr9pNڻE$Now{|rHrQβyiރ"JR"H!,"$kz MBov$C074 r1;5r=+콐||K<,WJt|hJ(Tn`?RP^܂d*w$U?Bخ_!ܯ+UJ"lb& ,T64@ 敥!X1lY5zRb2WΈ5g`5ܛ*lѳpFؙQB1 LyA_?% c$wUAemȡd{!i^G!'?>_0r_A8<1WDޢv:9$ /< ؞h)F`54>F&blLzB;! Fs?gd*ƓjD$);\WދGI1FU o!Ax=vmp;q8PΤ3w [}ުigaP;w'3GjA(e{]Bq&Tnx򒨰x=!!XtU$?dSF4A?bD[(g=O K͓:D(X% ie^գ]+~W|rg7! Nt4hNl DNmY"o`*[r^tTD<>'BU)VtI&[8s#+Yk/r"HzE4 e6xЁGX~˜} *}, x+tyԉm1c7֫+ж=b{'Qz9?>Hߢ}l MH{?dP-gjEg+VŜ(c03|v˃6^e8bX^J7Ǵ?WYJRRLjED4J@3"cH)bBsTB%3-ҳ (e];Oz E{PW i t3HhڒG+e/K[[E}zק5j={YՒx4K&V "]4h0wQ0!É|ǹVK-C"kI.Ɠqj&)sߖ |,9[/&eܞc{_+ajֶ&V X趡 &`xQuWn,Ĺ.v1{0HbsƫL˫yeoY1W $vuHftnِKiCs`iwTj $Nk X: p| W 4pL0쾹䟄MbX8FQFHdtl#_'`tx626b$qj0" U[|&ms9#cMH>}X\buA]\Dr9wp>1GΣk34nP߽E(znY1ɾ~G{~=nogtJɸ9ȊV]yƊZazJ_?2E~qsT#d*?F3ZnΣUMߺ|0bPJ*8F sG'V3 JzL[yJ^ƚ!s֒@G}8jZ! 3TBgXȖj݀KEa,*FDREYgn7VNq Q4[|\[ . #w~Kiri m#;&(msTGV#a=bXƫo _9bLMY3 ˁEC۪Fą. jꣲ:sSJs1`l7@PQ4sy&HLjbE'DŽde}ե$bĬrda>'xI?JSʚ(!0'{ Uq ׺eylMd'KL~\KfrEePĨO ѤʍʆNrjX['!lD3.sXװ ǧNS2XH3gX**̚n5@&΋>C2.TvARLC=R#b1:"?sҩ QKE+pe 䟟ɿ tN)N˟[7}tdt ex& ;,NE@vv3_ms/#.w/]9urxNBPyޅ%ȅ3@ʒ螘:ʶ>ʎfI(=WjVdz \[+Λ.,f~:GI duoAz W_jyy2e&Dр-GhIk{{^M и(M1xxc̙ۡZANMYw*gwI0atV %]RyKn$#F5bqäjd(0Z6n?fTGEУ2 xЋW.O1_cyEWyq Qg) -W i:k5o& B ?4Bgolang-github-cilium-ebpf-0.21.0+ds1/btf/traversal.go000066400000000000000000000065361520243672000223160ustar00rootroot00000000000000package btf import ( "fmt" "iter" ) // Functions to traverse a cyclic graph of types. The below was very useful: // https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/#post-order-and-reverse-post-order // postorder yields all types reachable from root in post order. func postorder(root Type, visited map[Type]struct{}) iter.Seq[Type] { return func(yield func(Type) bool) { visitInPostorder(root, visited, yield) } } // visitInPostorder is a separate function to avoid arguments escaping // to the heap. Don't change the setup without re-running the benchmarks. func visitInPostorder(root Type, visited map[Type]struct{}, yield func(typ Type) bool) bool { if _, ok := visited[root]; ok { return true } if visited == nil { visited = make(map[Type]struct{}) } visited[root] = struct{}{} for child := range children(root) { if !visitInPostorder(*child, visited, yield) { return false } } return yield(root) } // children yields all direct descendants of typ. func children(typ Type) iter.Seq[*Type] { return func(yield func(*Type) bool) { // Explicitly type switch on the most common types to allow the inliner to // do its work. This avoids allocating intermediate slices from walk() on // the heap. var tags []string switch v := typ.(type) { case *Void, *Int, *Enum, *Fwd, *Float, *declTag: // No children to traverse. // declTags is declared as a leaf type since it's parsed into .Tags fields of other types // during unmarshaling. case *Pointer: if !yield(&v.Target) { return } case *Array: if !yield(&v.Index) { return } if !yield(&v.Type) { return } case *Struct: for i := range v.Members { if !yield(&v.Members[i].Type) { return } for _, t := range v.Members[i].Tags { var tag Type = &declTag{v, t, i} if !yield(&tag) { return } } } tags = v.Tags case *Union: for i := range v.Members { if !yield(&v.Members[i].Type) { return } for _, t := range v.Members[i].Tags { var tag Type = &declTag{v, t, i} if !yield(&tag) { return } } } tags = v.Tags case *Typedef: if !yield(&v.Type) { return } tags = v.Tags case *Volatile: if !yield(&v.Type) { return } case *Const: if !yield(&v.Type) { return } case *Restrict: if !yield(&v.Type) { return } case *Func: if !yield(&v.Type) { return } if fp, ok := v.Type.(*FuncProto); ok { for i := range fp.Params { if len(v.ParamTags) <= i { continue } for _, t := range v.ParamTags[i] { var tag Type = &declTag{v, t, i} if !yield(&tag) { return } } } } tags = v.Tags case *FuncProto: if !yield(&v.Return) { return } for i := range v.Params { if !yield(&v.Params[i].Type) { return } } case *Var: if !yield(&v.Type) { return } tags = v.Tags case *Datasec: for i := range v.Vars { if !yield(&v.Vars[i].Type) { return } } case *TypeTag: if !yield(&v.Type) { return } case *cycle: // cycle has children, but we ignore them deliberately. default: panic(fmt.Sprintf("don't know how to walk Type %T", v)) } for _, t := range tags { var tag Type = &declTag{typ, t, -1} if !yield(&tag) { return } } } } golang-github-cilium-ebpf-0.21.0+ds1/btf/traversal_test.go000066400000000000000000000045261520243672000233520ustar00rootroot00000000000000package btf import ( "fmt" "testing" "github.com/go-quicktest/qt" ) func TestPostorderTraversal(t *testing.T) { ptr := newCyclicalType(2).(*Pointer) cst := ptr.Target.(*Const) str := cst.Type.(*Struct) t.Logf("%3v", ptr) pending := []Type{str, cst, ptr} for typ := range postorder(ptr, nil) { qt.Assert(t, qt.Equals(typ, pending[0])) pending = pending[1:] } qt.Assert(t, qt.HasLen(pending, 0)) i := &Int{Name: "foo"} // i appears twice at the same nesting depth. arr := &Array{Index: i, Type: i} seen := make(map[Type]bool) for typ := range postorder(arr, nil) { qt.Assert(t, qt.IsFalse(seen[typ])) seen[typ] = true } qt.Assert(t, qt.IsTrue(seen[arr])) qt.Assert(t, qt.IsTrue(seen[i])) } func TestPostorderTraversalVmlinux(t *testing.T) { spec := vmlinuxTestdataSpec(t) typ, err := spec.AnyTypeByName("gov_update_cpu_data") if err != nil { t.Fatal(err) } for _, typ := range []Type{typ} { t.Run(fmt.Sprintf("%s", typ), func(t *testing.T) { seen := make(map[Type]bool) var last Type for typ := range postorder(typ, nil) { if seen[typ] { t.Fatalf("%s visited twice", typ) } seen[typ] = true last = typ } if last != typ { t.Fatalf("Expected %s got %s as last type", typ, last) } for child := range children(typ) { qt.Check(t, qt.IsTrue(seen[*child]), qt.Commentf("missing child %s", *child)) } }) } } func TestChildren(t *testing.T) { for _, test := range []struct { typ Type count int }{ {&Int{}, 0}, {&Const{&Int{}}, 1}, {&Array{Index: &Int{}, Type: &Int{}}, 2}, } { t.Run(fmt.Sprint(test.typ), func(t *testing.T) { var count int allocs := testing.AllocsPerRun(1, func() { count = 0 for range children(test.typ) { count++ } }) qt.Assert(t, qt.Equals(count, test.count)) qt.Assert(t, qt.Equals(allocs, 0)) }) } } func BenchmarkPostorderTraversal(b *testing.B) { spec := vmlinuxTestdataSpec(b) var fn *Func err := spec.TypeByName("gov_update_cpu_data", &fn) if err != nil { b.Fatal(err) } for _, test := range []struct { name string typ Type }{ {"single type", &Int{}}, {"cycle(1)", newCyclicalType(1)}, {"cycle(10)", newCyclicalType(10)}, {"gov_update_cpu_data", fn}, } { b.Run(test.name, func(b *testing.B) { b.ReportAllocs() for b.Loop() { for range postorder(test.typ, nil) { } } }) } } golang-github-cilium-ebpf-0.21.0+ds1/btf/types.go000066400000000000000000000500471520243672000214530ustar00rootroot00000000000000package btf import ( "errors" "fmt" "io" "math" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) // Mirrors MAX_RESOLVE_DEPTH in libbpf. // https://github.com/libbpf/libbpf/blob/e26b84dc330c9644c07428c271ab491b0f01f4e1/src/btf.c#L761 const maxResolveDepth = 32 // TypeID identifies a type in a BTF section. type TypeID = sys.TypeID // Type represents a type described by BTF. // // A Type has three properties where compared to other Types. // // Identity: follows the [Go specification], two Types are considered identical // if they have the same concrete type and the same dynamic value, aka they point // at the same location in memory. This means that the following Types are // considered distinct even though they have the same "shape". // // a := &Int{Size: 1} // b := &Int{Size: 1} // a != b // // Equivalence: two Types are considered equivalent if they have the same shape // and thus are functionally interchangeable, even if they are located at different // memory addresses. The above two Int types are equivalent. // // Compatibility: two Types are considered compatible according to the rules of CO-RE // see [coreAreTypesCompatible] for details. This is a non-commutative property, // so A may be compatible with B, but B not compatible with A. // // [Go specification]: https://go.dev/ref/spec#Comparison_operators type Type interface { // Type can be formatted using the %s and %v verbs. %s outputs only the // identity of the type, without any detail. %v outputs additional detail. // // Use the '+' flag to include the address of the type. // // Use the width to specify how many levels of detail to output, for example // %1v will output detail for the root type and a short description of its // children. %2v would output details of the root type and its children // as well as a short description of the grandchildren. fmt.Formatter // Name of the type, empty for anonymous types and types that cannot // carry a name, like Void and Pointer. TypeName() string // Make a copy of the type, without copying Type members. copy() Type // New implementations must update children, deduper.typeHash, and typesEquivalent. } var ( _ Type = (*Int)(nil) _ Type = (*Struct)(nil) _ Type = (*Union)(nil) _ Type = (*Enum)(nil) _ Type = (*Fwd)(nil) _ Type = (*Func)(nil) _ Type = (*Typedef)(nil) _ Type = (*Var)(nil) _ Type = (*Datasec)(nil) _ Type = (*Float)(nil) _ Type = (*declTag)(nil) _ Type = (*TypeTag)(nil) _ Type = (*cycle)(nil) ) // Void is the unit type of BTF. type Void struct{} func (v *Void) Format(fs fmt.State, verb rune) { formatType(fs, verb, v) } func (v *Void) TypeName() string { return "" } func (v *Void) size() uint32 { return 0 } func (v *Void) copy() Type { return (*Void)(nil) } type IntEncoding byte // Valid IntEncodings. // // These may look like they are flags, but they aren't. const ( Unsigned IntEncoding = 0 Signed IntEncoding = 1 Char IntEncoding = 2 Bool IntEncoding = 4 ) func (ie IntEncoding) String() string { switch ie { case Char: // NB: There is no way to determine signedness for char. return "char" case Bool: return "bool" case Signed: return "signed" case Unsigned: return "unsigned" default: return fmt.Sprintf("IntEncoding(%d)", byte(ie)) } } // Int is an integer of a given length. // // See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int type Int struct { Name string // The size of the integer in bytes. Size uint32 Encoding IntEncoding } func (i *Int) Format(fs fmt.State, verb rune) { formatType(fs, verb, i, i.Encoding, "size=", i.Size) } func (i *Int) TypeName() string { return i.Name } func (i *Int) size() uint32 { return i.Size } func (i *Int) copy() Type { cpy := *i return &cpy } // Pointer is a pointer to another type. type Pointer struct { Target Type } func (p *Pointer) Format(fs fmt.State, verb rune) { formatType(fs, verb, p, "target=", p.Target) } func (p *Pointer) TypeName() string { return "" } func (p *Pointer) size() uint32 { return 8 } func (p *Pointer) copy() Type { cpy := *p return &cpy } // Array is an array with a fixed number of elements. type Array struct { Index Type Type Type Nelems uint32 } func (arr *Array) Format(fs fmt.State, verb rune) { formatType(fs, verb, arr, "index=", arr.Index, "type=", arr.Type, "n=", arr.Nelems) } func (arr *Array) TypeName() string { return "" } func (arr *Array) copy() Type { cpy := *arr return &cpy } // Struct is a compound type of consecutive members. type Struct struct { Name string // The size of the struct including padding, in bytes Size uint32 Members []Member Tags []string } func (s *Struct) Format(fs fmt.State, verb rune) { formatType(fs, verb, s, "fields=", len(s.Members)) } func (s *Struct) TypeName() string { return s.Name } func (s *Struct) size() uint32 { return s.Size } func (s *Struct) copy() Type { cpy := *s cpy.Members = copyMembers(s.Members) cpy.Tags = copyTags(cpy.Tags) return &cpy } func (s *Struct) members() []Member { return s.Members } // Union is a compound type where members occupy the same memory. type Union struct { Name string // The size of the union including padding, in bytes. Size uint32 Members []Member Tags []string } func (u *Union) Format(fs fmt.State, verb rune) { formatType(fs, verb, u, "fields=", len(u.Members)) } func (u *Union) TypeName() string { return u.Name } func (u *Union) size() uint32 { return u.Size } func (u *Union) copy() Type { cpy := *u cpy.Members = copyMembers(u.Members) cpy.Tags = copyTags(cpy.Tags) return &cpy } func (u *Union) members() []Member { return u.Members } func copyMembers(orig []Member) []Member { cpy := make([]Member, len(orig)) copy(cpy, orig) for i, member := range cpy { cpy[i].Tags = copyTags(member.Tags) } return cpy } func copyTags(orig []string) []string { if orig == nil { // preserve nil vs zero-len slice distinction return nil } cpy := make([]string, len(orig)) copy(cpy, orig) return cpy } type composite interface { Type members() []Member } var ( _ composite = (*Struct)(nil) _ composite = (*Union)(nil) ) // A value in bits. type Bits uint32 // Bytes converts a bit value into bytes. func (b Bits) Bytes() uint32 { return uint32(b / 8) } // Member is part of a Struct or Union. // // It is not a valid Type. type Member struct { Name string Type Type Offset Bits BitfieldSize Bits Tags []string } // Enum lists possible values. type Enum struct { Name string // Size of the enum value in bytes. Size uint32 // True if the values should be interpreted as signed integers. Signed bool Values []EnumValue } func (e *Enum) Format(fs fmt.State, verb rune) { formatType(fs, verb, e, "size=", e.Size, "values=", len(e.Values)) } func (e *Enum) TypeName() string { return e.Name } // EnumValue is part of an Enum // // Is is not a valid Type type EnumValue struct { Name string Value uint64 } func (e *Enum) size() uint32 { return e.Size } func (e *Enum) copy() Type { cpy := *e cpy.Values = make([]EnumValue, len(e.Values)) copy(cpy.Values, e.Values) return &cpy } // FwdKind is the type of forward declaration. type FwdKind int // Valid types of forward declaration. const ( FwdStruct FwdKind = iota FwdUnion ) func (fk FwdKind) String() string { switch fk { case FwdStruct: return "struct" case FwdUnion: return "union" default: return fmt.Sprintf("%T(%d)", fk, int(fk)) } } // Fwd is a forward declaration of a Type. type Fwd struct { Name string Kind FwdKind } func (f *Fwd) Format(fs fmt.State, verb rune) { formatType(fs, verb, f, f.Kind) } func (f *Fwd) TypeName() string { return f.Name } func (f *Fwd) copy() Type { cpy := *f return &cpy } func (f *Fwd) matches(typ Type) bool { if _, ok := As[*Struct](typ); ok && f.Kind == FwdStruct { return true } if _, ok := As[*Union](typ); ok && f.Kind == FwdUnion { return true } return false } // Typedef is an alias of a Type. type Typedef struct { Name string Type Type Tags []string } func (td *Typedef) Format(fs fmt.State, verb rune) { formatType(fs, verb, td, td.Type) } func (td *Typedef) TypeName() string { return td.Name } func (td *Typedef) copy() Type { cpy := *td cpy.Tags = copyTags(td.Tags) return &cpy } // Volatile is a qualifier. type Volatile struct { Type Type } func (v *Volatile) Format(fs fmt.State, verb rune) { formatType(fs, verb, v, v.Type) } func (v *Volatile) TypeName() string { return "" } func (v *Volatile) qualify() Type { return v.Type } func (v *Volatile) copy() Type { cpy := *v return &cpy } // Const is a qualifier. type Const struct { Type Type } func (c *Const) Format(fs fmt.State, verb rune) { formatType(fs, verb, c, c.Type) } func (c *Const) TypeName() string { return "" } func (c *Const) qualify() Type { return c.Type } func (c *Const) copy() Type { cpy := *c return &cpy } // Restrict is a qualifier. type Restrict struct { Type Type } func (r *Restrict) Format(fs fmt.State, verb rune) { formatType(fs, verb, r, r.Type) } func (r *Restrict) TypeName() string { return "" } func (r *Restrict) qualify() Type { return r.Type } func (r *Restrict) copy() Type { cpy := *r return &cpy } // Func is a function definition. type Func struct { Name string Type Type Linkage FuncLinkage Tags []string // ParamTags holds a list of tags for each parameter of the FuncProto to which `Type` points. // If no tags are present for any param, the outer slice will be nil/len(ParamTags)==0. // If at least 1 param has a tag, the outer slice will have the same length as the number of params. // The inner slice contains the tags and may be nil/len(ParamTags[i])==0 if no tags are present for that param. ParamTags [][]string } type funcInfoMeta struct{} func FuncMetadata(ins *asm.Instruction) *Func { fn, _ := ins.Metadata.Get(funcInfoMeta{}).(*Func) return fn } // WithFuncMetadata adds a btf.Func to the Metadata of asm.Instruction. func WithFuncMetadata(ins asm.Instruction, fn *Func) asm.Instruction { ins.Metadata.Set(funcInfoMeta{}, fn) return ins } func (f *Func) Format(fs fmt.State, verb rune) { formatType(fs, verb, f, f.Linkage, "proto=", f.Type) } func (f *Func) TypeName() string { return f.Name } func (f *Func) copy() Type { cpy := *f cpy.Tags = copyTags(f.Tags) if f.ParamTags != nil { // preserve nil vs zero-len slice distinction ptCopy := make([][]string, len(f.ParamTags)) for i, tags := range f.ParamTags { ptCopy[i] = copyTags(tags) } cpy.ParamTags = ptCopy } return &cpy } // FuncProto is a function declaration. type FuncProto struct { Return Type Params []FuncParam } func (fp *FuncProto) Format(fs fmt.State, verb rune) { formatType(fs, verb, fp, "args=", len(fp.Params), "return=", fp.Return) } func (fp *FuncProto) TypeName() string { return "" } func (fp *FuncProto) copy() Type { cpy := *fp cpy.Params = make([]FuncParam, len(fp.Params)) copy(cpy.Params, fp.Params) return &cpy } type FuncParam struct { Name string Type Type } // Var is a global variable. type Var struct { Name string Type Type Linkage VarLinkage Tags []string } func (v *Var) Format(fs fmt.State, verb rune) { formatType(fs, verb, v, v.Linkage) } func (v *Var) TypeName() string { return v.Name } func (v *Var) copy() Type { cpy := *v cpy.Tags = copyTags(v.Tags) return &cpy } // Datasec is a global program section containing data. type Datasec struct { Name string Size uint32 Vars []VarSecinfo } func (ds *Datasec) Format(fs fmt.State, verb rune) { formatType(fs, verb, ds) } func (ds *Datasec) TypeName() string { return ds.Name } func (ds *Datasec) size() uint32 { return ds.Size } func (ds *Datasec) copy() Type { cpy := *ds cpy.Vars = make([]VarSecinfo, len(ds.Vars)) copy(cpy.Vars, ds.Vars) return &cpy } // VarSecinfo describes variable in a Datasec. // // It is not a valid Type. type VarSecinfo struct { // Var or Func. Type Type Offset uint32 Size uint32 } // Float is a float of a given length. type Float struct { Name string // The size of the float in bytes. Size uint32 } func (f *Float) Format(fs fmt.State, verb rune) { formatType(fs, verb, f, "size=", f.Size*8) } func (f *Float) TypeName() string { return f.Name } func (f *Float) size() uint32 { return f.Size } func (f *Float) copy() Type { cpy := *f return &cpy } // declTag associates metadata with a declaration. type declTag struct { Type Type Value string // The index this tag refers to in the target type. For composite types, // a value of -1 indicates that the tag refers to the whole type. Otherwise // it indicates which member or argument the tag applies to. Index int } func (dt *declTag) Format(fs fmt.State, verb rune) { formatType(fs, verb, dt, "type=", dt.Type, "value=", dt.Value, "index=", dt.Index) } func (dt *declTag) TypeName() string { return "" } func (dt *declTag) copy() Type { cpy := *dt return &cpy } // TypeTag associates metadata with a pointer type. Tag types act as a custom // modifier(const, restrict, volatile) for the target type. Unlike declTags, // TypeTags are ordered so the order in which they are added matters. // // One of their uses is to mark pointers as `__kptr` meaning a pointer points // to kernel memory. Adding a `__kptr` to pointers in map values allows you // to store pointers to kernel memory in maps. type TypeTag struct { Type Type Value string } func (tt *TypeTag) Format(fs fmt.State, verb rune) { formatType(fs, verb, tt, "type=", tt.Type, "value=", tt.Value) } func (tt *TypeTag) TypeName() string { return "" } func (tt *TypeTag) qualify() Type { return tt.Type } func (tt *TypeTag) copy() Type { cpy := *tt return &cpy } // cycle is a type which had to be elided since it exceeded maxTypeDepth. type cycle struct { root Type } func (c *cycle) ID() TypeID { return math.MaxUint32 } func (c *cycle) Format(fs fmt.State, verb rune) { formatType(fs, verb, c, "root=", c.root) } func (c *cycle) TypeName() string { return "" } func (c *cycle) copy() Type { cpy := *c return &cpy } type sizer interface { size() uint32 } var ( _ sizer = (*Int)(nil) _ sizer = (*Pointer)(nil) _ sizer = (*Struct)(nil) _ sizer = (*Union)(nil) _ sizer = (*Enum)(nil) _ sizer = (*Datasec)(nil) ) type qualifier interface { qualify() Type } var ( _ qualifier = (*Const)(nil) _ qualifier = (*Restrict)(nil) _ qualifier = (*Volatile)(nil) _ qualifier = (*TypeTag)(nil) ) var errUnsizedType = errors.New("type is unsized") // Sizeof returns the size of a type in bytes. // // Returns an error if the size can't be computed. func Sizeof(typ Type) (int, error) { var ( n = int64(1) elem int64 ) for i := 0; i < maxResolveDepth; i++ { switch v := typ.(type) { case *Array: if n > 0 && int64(v.Nelems) > math.MaxInt64/n { return 0, fmt.Errorf("type %s: overflow", typ) } // Arrays may be of zero length, which allows // n to be zero as well. n *= int64(v.Nelems) typ = v.Type continue case sizer: elem = int64(v.size()) case *Typedef: typ = v.Type continue case qualifier: typ = v.qualify() continue default: return 0, fmt.Errorf("type %T: %w", typ, errUnsizedType) } if n > 0 && elem > math.MaxInt64/n { return 0, fmt.Errorf("type %s: overflow", typ) } size := n * elem if int64(int(size)) != size { return 0, fmt.Errorf("type %s: overflow", typ) } return int(size), nil } return 0, fmt.Errorf("type %s: exceeded type depth", typ) } // alignof returns the alignment of a type. // // Returns an error if the Type can't be aligned, like an integer with an uneven // size. Currently only supports the subset of types necessary for bitfield // relocations. func alignof(typ Type) (int, error) { var n int switch t := UnderlyingType(typ).(type) { case *Enum: n = int(t.size()) case *Int: n = int(t.Size) case *Array: return alignof(t.Type) default: return 0, fmt.Errorf("can't calculate alignment of %T", t) } if !internal.IsPow(n) { return 0, fmt.Errorf("alignment value %d is not a power of two", n) } return n, nil } // Copy a Type recursively. // // typ may form a cycle. func Copy(typ Type) Type { return copyType(typ, nil, make(map[Type]Type), nil) } func copyType(typ Type, ids map[Type]TypeID, copies map[Type]Type, copiedIDs map[Type]TypeID) Type { if typ == nil { return nil } cpy, ok := copies[typ] if ok { // This has been copied previously, no need to continue. return cpy } cpy = typ.copy() copies[typ] = cpy if id, ok := ids[typ]; ok { copiedIDs[cpy] = id } for child := range children(cpy) { *child = copyType(*child, ids, copies, copiedIDs) } return cpy } type typeDeque = internal.Deque[*Type] // essentialName represents the name of a BTF type stripped of any flavor // suffixes after a ___ delimiter. type essentialName string // newEssentialName returns name without a ___ suffix. // // CO-RE has the concept of 'struct flavors', which are used to deal with // changes in kernel data structures. Anything after three underscores // in a type name is ignored for the purpose of finding a candidate type // in the kernel's BTF. func newEssentialName(name string) essentialName { if name == "" { return "" } lastIdx := strings.LastIndex(name, "___") if lastIdx > 0 { return essentialName(name[:lastIdx]) } return essentialName(name) } // UnderlyingType skips qualifiers and Typedefs. func UnderlyingType(typ Type) Type { result := typ for depth := 0; depth <= maxResolveDepth; depth++ { switch v := (result).(type) { case qualifier: result = v.qualify() case *Typedef: result = v.Type default: return result } } return &cycle{typ} } // QualifiedType returns the type with all qualifiers removed. func QualifiedType(typ Type) Type { result := typ for depth := 0; depth <= maxResolveDepth; depth++ { switch v := (result).(type) { case qualifier: result = v.qualify() default: return result } } return &cycle{typ} } // As returns typ if is of type T. Otherwise it peels qualifiers and Typedefs // until it finds a T. // // Returns the zero value and false if there is no T or if the type is nested // too deeply. func As[T Type](typ Type) (T, bool) { // NB: We can't make this function return (*T) since then // we can't assert that a type matches an interface which // embeds Type: as[composite](T). for depth := 0; depth <= maxResolveDepth; depth++ { switch v := (typ).(type) { case T: return v, true case qualifier: typ = v.qualify() case *Typedef: typ = v.Type default: goto notFound } } notFound: var zero T return zero, false } type formatState struct { fmt.State depth int } // formattableType is a subset of Type, to ease unit testing of formatType. type formattableType interface { fmt.Formatter TypeName() string } // formatType formats a type in a canonical form. // // Handles cyclical types by only printing cycles up to a certain depth. Elements // in extra are separated by spaces unless the preceding element is a string // ending in '='. func formatType(f fmt.State, verb rune, t formattableType, extra ...interface{}) { if verb != 'v' && verb != 's' { fmt.Fprintf(f, "{UNRECOGNIZED: %c}", verb) return } _, _ = io.WriteString(f, internal.GoTypeName(t)) if name := t.TypeName(); name != "" { // Output BTF type name if present. fmt.Fprintf(f, ":%q", name) } if f.Flag('+') { // Output address if requested. fmt.Fprintf(f, ":%#p", t) } if verb == 's' { // %s omits details. return } var depth int if ps, ok := f.(*formatState); ok { depth = ps.depth f = ps.State } maxDepth, ok := f.Width() if !ok { maxDepth = 0 } if depth > maxDepth { // We've reached the maximum depth. This avoids infinite recursion even // for cyclical types. return } if len(extra) == 0 { return } wantSpace := false _, _ = io.WriteString(f, "[") for _, arg := range extra { if wantSpace { _, _ = io.WriteString(f, " ") } switch v := arg.(type) { case string: _, _ = io.WriteString(f, v) wantSpace = len(v) > 0 && v[len(v)-1] != '=' continue case formattableType: v.Format(&formatState{f, depth + 1}, verb) default: fmt.Fprint(f, arg) } wantSpace = true } _, _ = io.WriteString(f, "]") } golang-github-cilium-ebpf-0.21.0+ds1/btf/types_test.go000066400000000000000000000336521520243672000225150ustar00rootroot00000000000000package btf import ( "encoding/binary" "fmt" "reflect" "testing" "github.com/go-quicktest/qt" "github.com/google/go-cmp/cmp" "github.com/cilium/ebpf/internal/testutils" ) func TestSizeof(t *testing.T) { testcases := []struct { size int typ Type }{ {0, (*Void)(nil)}, {1, &Int{Size: 1}}, {8, &Enum{Size: 8}}, {0, &Array{Type: &Pointer{Target: (*Void)(nil)}, Nelems: 0}}, {12, &Array{Type: &Enum{Size: 4}, Nelems: 3}}, } for _, tc := range testcases { name := fmt.Sprint(tc.typ) t.Run(name, func(t *testing.T) { have, err := Sizeof(tc.typ) if err != nil { t.Fatal("Can't calculate size:", err) } if have != tc.size { t.Errorf("Expected size %d, got %d", tc.size, have) } }) } } func TestCopy(t *testing.T) { i := &Int{Size: 4} tags := []string{"bar:foo"} got := Copy(&Struct{ Members: []Member{ {Name: "a", Type: i}, {Name: "b", Type: i}, }, }) members := got.(*Struct).Members qt.Check(t, qt.Equals(members[0].Type.(*Int), members[1].Type.(*Int)), qt.Commentf("identity should be preserved")) for _, test := range []struct { name string typ Type }{ {"nil", nil}, {"void", (*Void)(nil)}, {"int", i}, {"cyclical", newCyclicalType(2)}, {"struct tags", &Struct{Tags: tags, Members: []Member{{Tags: tags}}}}, {"union tags", &Union{Tags: tags, Members: []Member{{Tags: tags}}}}, {"typedef tags", &Typedef{Type: i, Tags: tags}}, {"var tags", &Var{Type: i, Tags: tags}}, {"func tags", &Func{Tags: tags, ParamTags: [][]string{tags}}}, } { t.Run(test.name, func(t *testing.T) { cpy := Copy(test.typ) qt.Assert(t, testutils.IsDeepCopy(cpy, test.typ)) }) } } func TestAs(t *testing.T) { i := &Int{} ptr := &Pointer{i} td := &Typedef{Type: ptr} cst := &Const{td} vol := &Volatile{cst} // It's possible to retrieve qualifiers and Typedefs. haveVol, ok := As[*Volatile](vol) qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(haveVol, vol)) haveTd, ok := As[*Typedef](vol) qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(haveTd, td)) haveCst, ok := As[*Const](vol) qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(haveCst, cst)) // Make sure we don't skip Pointer. haveI, ok := As[*Int](vol) qt.Assert(t, qt.IsFalse(ok)) qt.Assert(t, qt.IsNil(haveI)) // Make sure we can always retrieve Pointer. for _, typ := range []Type{ td, cst, vol, ptr, } { have, ok := As[*Pointer](typ) qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(have, ptr)) } } func BenchmarkCopy(b *testing.B) { typ := newCyclicalType(10) b.ReportAllocs() for b.Loop() { Copy(typ) } } // The following are valid Types. // // There currently is no better way to document which // types implement an interface. func ExampleType_validTypes() { var _ Type = &Void{} var _ Type = &Int{} var _ Type = &Pointer{} var _ Type = &Array{} var _ Type = &Struct{} var _ Type = &Union{} var _ Type = &Enum{} var _ Type = &Fwd{} var _ Type = &Typedef{} var _ Type = &Volatile{} var _ Type = &Const{} var _ Type = &Restrict{} var _ Type = &Func{} var _ Type = &FuncProto{} var _ Type = &Var{} var _ Type = &Datasec{} var _ Type = &Float{} } func TestType(t *testing.T) { types := []func() Type{ func() Type { return &Void{} }, func() Type { return &Int{Size: 2} }, func() Type { return &Pointer{Target: &Void{}} }, func() Type { return &Array{Type: &Int{}} }, func() Type { return &Struct{ Members: []Member{{Type: &Void{}}}, } }, func() Type { return &Union{ Members: []Member{{Type: &Void{}}}, } }, func() Type { return &Enum{} }, func() Type { return &Fwd{Name: "thunk"} }, func() Type { return &Typedef{Type: &Void{}} }, func() Type { return &Volatile{Type: &Void{}} }, func() Type { return &Const{Type: &Void{}} }, func() Type { return &Restrict{Type: &Void{}} }, func() Type { return &Func{Name: "foo", Type: &Void{}} }, func() Type { return &FuncProto{ Params: []FuncParam{{Name: "bar", Type: &Void{}}}, Return: &Void{}, } }, func() Type { return &Var{Type: &Void{}} }, func() Type { return &Datasec{ Vars: []VarSecinfo{{Type: &Void{}}}, } }, func() Type { return &Float{} }, func() Type { return &TypeTag{Type: &Void{}} }, func() Type { return &cycle{&Void{}} }, } compareTypes := cmp.Comparer(func(a, b *Type) bool { return a == b }) for _, fn := range types { typ := fn() t.Run(fmt.Sprintf("%T", typ), func(t *testing.T) { t.Logf("%v", typ) if typ == typ.copy() { t.Error("Copy doesn't copy") } var a []*Type for t := range children(typ) { a = append(a, t) } if _, ok := typ.(*cycle); !ok { if n := countChildren(t, reflect.TypeOf(typ)); len(a) < n { t.Errorf("walkType visited %d children, expected at least %d", len(a), n) } } var b []*Type for t := range children(typ) { b = append(b, t) } if diff := cmp.Diff(a, b, compareTypes); diff != "" { t.Errorf("Walk mismatch (-want +got):\n%s", diff) } }) } } func TestTagMarshaling(t *testing.T) { for _, typ := range []Type{ &TypeTag{&Int{}, "foo"}, &Struct{Members: []Member{ {Type: &Int{}, Tags: []string{"bar"}}, }, Tags: []string{"foo"}}, &Union{Members: []Member{ {Type: &Int{}, Tags: []string{"bar"}}, {Type: &Int{}, Tags: []string{"baz"}}, }, Tags: []string{"foo"}}, &Func{Type: &FuncProto{Return: &Int{}, Params: []FuncParam{ {Name: "param1", Type: &Int{}}, }}, Tags: []string{"foo"}, ParamTags: [][]string{{"bar"}}}, &Var{Name: "var1", Type: &Int{}, Tags: []string{"foo"}}, &Typedef{Name: "baz", Type: &Int{}, Tags: []string{"foo"}}, } { t.Run(fmt.Sprint(typ), func(t *testing.T) { s := specFromTypes(t, []Type{typ}) have, err := s.TypeByID(1) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(have, typ)) }) } } func countChildren(t *testing.T, typ reflect.Type) int { if typ.Kind() != reflect.Pointer { t.Fatal("Expected pointer, got", typ.Kind()) } typ = typ.Elem() if typ.Kind() != reflect.Struct { t.Fatal("Expected struct, got", typ.Kind()) } var n int for i := 0; i < typ.NumField(); i++ { if typ.Field(i).Type == reflect.TypeOf((*Type)(nil)).Elem() { n++ } } return n } type testFormattableType struct { name string extra []interface{} } var _ formattableType = (*testFormattableType)(nil) func (tft *testFormattableType) TypeName() string { return tft.name } func (tft *testFormattableType) Format(fs fmt.State, verb rune) { formatType(fs, verb, tft, tft.extra...) } func TestFormatType(t *testing.T) { t1 := &testFormattableType{"", []interface{}{"extra"}} t1Addr := fmt.Sprintf("%#p", t1) goType := reflect.TypeOf(t1).Elem().Name() t2 := &testFormattableType{"foo", []interface{}{t1}} t3 := &testFormattableType{extra: []interface{}{""}} tests := []struct { t formattableType fmt string contains []string omits []string }{ // %s doesn't contain address or extra. {t1, "%s", []string{goType}, []string{t1Addr, "extra"}}, // %+s doesn't contain extra. {t1, "%+s", []string{goType, t1Addr}, []string{"extra"}}, // %v does contain extra. {t1, "%v", []string{goType, "extra"}, []string{t1Addr}}, // %+v does contain address. {t1, "%+v", []string{goType, "extra", t1Addr}, nil}, // %v doesn't print nested types' extra. {t2, "%v", []string{goType, t2.name}, []string{"extra"}}, // %1v does print nested types' extra. {t2, "%1v", []string{goType, t2.name, "extra"}, nil}, // empty strings in extra don't emit anything. {t3, "%v", []string{"[]"}, nil}, } for _, test := range tests { t.Run(test.fmt, func(t *testing.T) { str := fmt.Sprintf(test.fmt, test.t) t.Log(str) for _, want := range test.contains { qt.Assert(t, qt.StringContains(str, want)) } for _, notWant := range test.omits { qt.Assert(t, qt.Not(qt.StringContains(str, notWant))) } }) } } func newCyclicalType(n int) Type { ptr := &Pointer{} prev := Type(ptr) for i := 0; i < n; i++ { switch i % 5 { case 0: prev = &Struct{ Members: []Member{ {Type: prev}, }, } case 1: prev = &Const{Type: prev} case 2: prev = &Volatile{Type: prev} case 3: prev = &Typedef{Type: prev} case 4: prev = &Array{Type: prev, Index: &Int{Size: 1}} } } ptr.Target = prev return ptr } func TestUnderlyingType(t *testing.T) { wrappers := []struct { name string fn func(Type) Type }{ {"const", func(t Type) Type { return &Const{Type: t} }}, {"volatile", func(t Type) Type { return &Volatile{Type: t} }}, {"restrict", func(t Type) Type { return &Restrict{Type: t} }}, {"typedef", func(t Type) Type { return &Typedef{Type: t} }}, {"type tag", func(t Type) Type { return &TypeTag{Type: t} }}, } for _, test := range wrappers { t.Run(test.name+" cycle", func(t *testing.T) { root := &Volatile{} root.Type = test.fn(root) got, ok := UnderlyingType(root).(*cycle) qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals[Type](got.root, root)) }) } for _, test := range wrappers { t.Run(test.name, func(t *testing.T) { want := &Int{} got := UnderlyingType(test.fn(want)) qt.Assert(t, qt.Equals[Type](got, want)) }) } } func TestInflateLegacyBitfield(t *testing.T) { const offset = 3 const size = 5 addHeaderAndStringTable := func(types ...any) []byte { var buf []byte var err error for _, typ := range types { buf, err = binary.Append(buf, binary.LittleEndian, typ) qt.Assert(t, qt.IsNil(err)) } header, err := binary.Append(nil, binary.LittleEndian, &btfHeader{ Magic: btfMagic, Version: 1, Flags: 0, HdrLen: uint32(btfHeaderLen), TypeOff: 0, TypeLen: uint32(len(buf)), StringOff: uint32(len(buf)), StringLen: 1, }) qt.Assert(t, qt.IsNil(err)) buf = append(header, buf...) buf = append(buf, 0) // string table return buf } var placeholder struct { btfType btfInt } placeholder.SetKind(kindInt) placeholder.SetSize(4) placeholder.SetOffset(offset) placeholder.SetBits(size) var structFirst struct { btfType Members [1]btfMember } structFirst.SetKind(kindStruct) structFirst.SetVlen(1) structFirst.Members = [...]btfMember{{Type: 2}} before := addHeaderAndStringTable(&structFirst, &placeholder) structSecond := structFirst structSecond.Members = [...]btfMember{{Type: 1}} after := addHeaderAndStringTable(&placeholder, &structSecond) for _, test := range []struct { name string buf []byte }{ {"struct before int", before}, {"struct after int", after}, } { t.Run(test.name, func(t *testing.T) { spec, err := loadRawSpec(test.buf, nil) qt.Assert(t, qt.IsNil(err)) for _, typ := range typesFromSpec(t, spec) { s, ok := typ.(*Struct) if !ok { continue } i := s.Members[0] if i.BitfieldSize != size { t.Errorf("Expected bitfield size %d, got %d", size, i.BitfieldSize) } if i.Offset != offset { t.Errorf("Expected offset %d, got %d", offset, i.Offset) } return } t.Fatal("No Struct returned from inflateRawTypes") }) } } func BenchmarkWalk(b *testing.B) { types := []Type{ &Void{}, &Int{}, &Pointer{}, &Array{}, &Struct{Members: make([]Member, 2)}, &Union{Members: make([]Member, 2)}, &Enum{}, &Fwd{}, &Typedef{}, &Volatile{}, &Const{}, &Restrict{}, &Func{}, &FuncProto{Params: make([]FuncParam, 2)}, &Var{}, &Datasec{Vars: make([]VarSecinfo, 2)}, } for _, typ := range types { b.Run(fmt.Sprint(typ), func(b *testing.B) { b.ReportAllocs() for b.Loop() { var dq typeDeque for child := range children(typ) { dq.Push(child) } } }) } } func TestTagUnmarshaling(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/tags-*.elf"), func(t *testing.T, file string) { spec, err := LoadSpec(file) qt.Assert(t, qt.IsNil(err)) var s *Struct err = spec.TypeByName("s", &s) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(s.Tags, []string{"c"})) qt.Assert(t, qt.ContentEquals(s.Members[0].Tags, []string{"a"})) qt.Assert(t, qt.ContentEquals(s.Members[1].Tags, []string{"b"})) var u *Union err = spec.TypeByName("u", &u) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(u.Tags, []string{"c"})) qt.Assert(t, qt.ContentEquals(u.Members[0].Tags, []string{"a"})) qt.Assert(t, qt.ContentEquals(u.Members[1].Tags, []string{"b"})) var td *Typedef err = spec.TypeByName("td", &td) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(td.Tags, []string{"b"})) var s1 *Var err = spec.TypeByName("s1", &s1) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(s1.Tags, []string{"d"})) var s2 *Var err = spec.TypeByName("u1", &s2) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(s2.Tags, []string{"e"})) var t1 *Var err = spec.TypeByName("t1", &t1) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(t1.Tags, []string{"a"})) var extFunc *Func err = spec.TypeByName("fwdDecl", &extFunc) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(extFunc.Tags, []string{"a", "b"})) qt.Assert(t, qt.ContentEquals(extFunc.ParamTags, [][]string{{"c"}, {"d"}})) var normalFunc *Func err = spec.TypeByName("normalDecl1", &normalFunc) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(normalFunc.Tags, []string{"e"})) qt.Assert(t, qt.ContentEquals(normalFunc.ParamTags, [][]string{{"b"}, {"c"}})) err = spec.TypeByName("normalDecl2", &normalFunc) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(normalFunc.Tags, []string{"e"})) qt.Assert(t, qt.ContentEquals(normalFunc.ParamTags, [][]string{{"b"}, {"c"}})) }) } func BenchmarkUnderlyingType(b *testing.B) { b.Run("no unwrapping", func(b *testing.B) { v := &Int{} b.ReportAllocs() for b.Loop() { UnderlyingType(v) } }) b.Run("single unwrapping", func(b *testing.B) { v := &Typedef{Type: &Int{}} b.ReportAllocs() for b.Loop() { UnderlyingType(v) } }) } // As can be used to strip qualifiers from a Type. func ExampleAs() { a := &Volatile{Type: &Pointer{Target: &Typedef{Name: "foo", Type: &Int{Size: 2}}}} fmt.Println(As[*Pointer](a)) // Output: Pointer[target=Typedef:"foo"] true } golang-github-cilium-ebpf-0.21.0+ds1/btf/unmarshal.go000066400000000000000000000445641520243672000223100ustar00rootroot00000000000000package btf import ( "bytes" "encoding/binary" "fmt" "hash/maphash" "io" "iter" "maps" "math" "slices" "sync" ) // sharedBuf is a buffer which may be shared between multiple decoders. // // It must not be modified. Some sharedBuf may be backed by an mmap-ed file, in // which case the sharedBuf has a finalizer. sharedBuf must therefore always be // passed as a pointer. type sharedBuf struct { raw []byte } type decoder struct { // Immutable fields, may be shared. base *decoder byteOrder binary.ByteOrder *sharedBuf strings *stringTable // The ID for offsets[0]. firstTypeID TypeID // Map from TypeID to offset of the marshaled data in raw. Contains an entry // for each TypeID, including 0 aka Void. The offset for Void is invalid. offsets []int declTags map[TypeID][]TypeID // An index from essentialName to TypeID. namedTypes *fuzzyStringIndex // Protection for mutable fields below. mu sync.Mutex types map[TypeID]Type typeIDs map[Type]TypeID legacyBitfields map[TypeID][2]Bits // offset, size } func newDecoder(raw []byte, bo binary.ByteOrder, strings *stringTable, base *decoder) (*decoder, error) { firstTypeID := TypeID(0) if base != nil { if base.byteOrder != bo { return nil, fmt.Errorf("can't use %v base with %v split BTF", base.byteOrder, bo) } if base.firstTypeID != 0 { return nil, fmt.Errorf("can't use split BTF as base") } firstTypeID = TypeID(len(base.offsets)) } var header btfType var numTypes, numDeclTags, numNamedTypes int for _, err := range allBtfTypeOffsets(raw, bo, &header) { if err != nil { return nil, err } numTypes++ if header.Kind() == kindDeclTag { numDeclTags++ } if header.NameOff != 0 { numNamedTypes++ } } if firstTypeID == 0 { // Allocate an extra slot for Void so we don't have to deal with // constant off by one issues. numTypes++ } offsets := make([]int, 0, numTypes) declTags := make(map[TypeID][]TypeID, numDeclTags) namedTypes := newFuzzyStringIndex(numNamedTypes) if firstTypeID == 0 { // Add a sentinel for Void. offsets = append(offsets, math.MaxInt) } id := firstTypeID + TypeID(len(offsets)) for offset := range allBtfTypeOffsets(raw, bo, &header) { if id < firstTypeID { return nil, fmt.Errorf("no more type IDs") } offsets = append(offsets, offset) if header.Kind() == kindDeclTag { declTags[header.Type()] = append(declTags[header.Type()], id) } // Build named type index. name, err := strings.LookupBytes(header.NameOff) if err != nil { return nil, fmt.Errorf("lookup type name for id %v: %w", id, err) } if len(name) > 0 { if i := bytes.Index(name, []byte("___")); i != -1 { // Flavours are rare. It's cheaper to find the first index for some // reason. i = bytes.LastIndex(name, []byte("___")) name = name[:i] } namedTypes.Add(name, id) } id++ } namedTypes.Build() return &decoder{ base, bo, &sharedBuf{raw}, strings, firstTypeID, offsets, declTags, namedTypes, sync.Mutex{}, make(map[TypeID]Type), make(map[Type]TypeID), make(map[TypeID][2]Bits), }, nil } func allBtfTypeOffsets(buf []byte, bo binary.ByteOrder, header *btfType) iter.Seq2[int, error] { return func(yield func(int, error) bool) { for offset := 0; offset < len(buf); { start := offset n, err := unmarshalBtfType(header, buf[offset:], bo) if err != nil { yield(-1, fmt.Errorf("unmarshal type header: %w", err)) return } offset += n n, err = header.DataLen() if err != nil { yield(-1, err) return } offset += n if offset > len(buf) { yield(-1, fmt.Errorf("auxiliary type data: %w", io.ErrUnexpectedEOF)) return } if !yield(start, nil) { return } } } } func rebaseDecoder(d *decoder, base *decoder) (*decoder, error) { if d.base == nil { return nil, fmt.Errorf("rebase split spec: not a split spec") } if len(d.base.raw) != len(base.raw) || (len(d.base.raw) > 0 && &d.base.raw[0] != &base.raw[0]) { return nil, fmt.Errorf("rebase split spec: raw BTF differs") } return &decoder{ base, d.byteOrder, d.sharedBuf, d.strings, d.firstTypeID, d.offsets, d.declTags, d.namedTypes, sync.Mutex{}, make(map[TypeID]Type), make(map[Type]TypeID), make(map[TypeID][2]Bits), }, nil } // Copy performs a deep copy of a decoder and its base. func (d *decoder) Copy() *decoder { if d == nil { return nil } return d.copy(nil) } func (d *decoder) copy(copiedTypes map[Type]Type) *decoder { if d == nil { return nil } d.mu.Lock() defer d.mu.Unlock() if copiedTypes == nil { copiedTypes = make(map[Type]Type, len(d.types)) } types := make(map[TypeID]Type, len(d.types)) typeIDs := make(map[Type]TypeID, len(d.typeIDs)) for id, typ := range d.types { types[id] = copyType(typ, d.typeIDs, copiedTypes, typeIDs) } return &decoder{ d.base.copy(copiedTypes), d.byteOrder, d.sharedBuf, d.strings, d.firstTypeID, d.offsets, d.declTags, d.namedTypes, sync.Mutex{}, types, typeIDs, maps.Clone(d.legacyBitfields), } } // TypeID returns the ID for a Type previously obtained via [TypeByID]. func (d *decoder) TypeID(typ Type) (TypeID, error) { if _, ok := typ.(*Void); ok { // Equality is weird for void, since it is a zero sized type. return 0, nil } d.mu.Lock() defer d.mu.Unlock() id, ok := d.typeIDs[typ] if !ok { return 0, fmt.Errorf("no ID for type %s: %w", typ, ErrNotFound) } return id, nil } // TypesByName returns all types which have the given essential name. // // Returns ErrNotFound if no matching Type exists. func (d *decoder) TypesByName(name essentialName) ([]Type, error) { var types []Type for id := range d.namedTypes.Find(string(name)) { typ, err := d.TypeByID(id) if err != nil { return nil, err } if newEssentialName(typ.TypeName()) == name { // Deal with hash collisions by checking against the name. types = append(types, typ) } } if len(types) == 0 { // Return an unwrapped error because this is on the hot path // for CO-RE. return nil, ErrNotFound } return types, nil } // TypeByID decodes a type and any of its descendants. func (d *decoder) TypeByID(id TypeID) (Type, error) { d.mu.Lock() defer d.mu.Unlock() return d.inflateType(id) } func (d *decoder) inflateType(id TypeID) (typ Type, err error) { defer func() { if r := recover(); r != nil { err = r.(error) } // err is the return value of the enclosing function, even if an explicit // return is used. // See https://go.dev/ref/spec#Defer_statements if err != nil { // Remove partially inflated type so that d.types only contains // fully inflated ones. delete(d.types, id) } else { // Populate reverse index. d.typeIDs[typ] = id } }() if id < d.firstTypeID { return d.base.inflateType(id) } if id == 0 { // Void is defined to always be type ID 0, and is thus omitted from BTF. // Fast-path because it is looked up frequently. return (*Void)(nil), nil } if typ, ok := d.types[id]; ok { return typ, nil } fixup := func(id TypeID, typ *Type) { fixup, err := d.inflateType(id) if err != nil { panic(err) } *typ = fixup } convertMembers := func(header *btfType, buf []byte) ([]Member, error) { var bm btfMember members := make([]Member, 0, header.Vlen()) for i := range header.Vlen() { n, err := unmarshalBtfMember(&bm, buf, d.byteOrder) if err != nil { return nil, fmt.Errorf("unmarshal member: %w", err) } buf = buf[n:] name, err := d.strings.Lookup(bm.NameOff) if err != nil { return nil, fmt.Errorf("can't get name for member %d: %w", i, err) } members = append(members, Member{ Name: name, Offset: Bits(bm.Offset), }) m := &members[i] fixup(bm.Type, &m.Type) if header.Bitfield() { m.BitfieldSize = Bits(bm.Offset >> 24) m.Offset &= 0xffffff // We ignore legacy bitfield definitions if the current composite // is a new-style bitfield. This is kind of safe since offset and // size on the type of the member must be zero if kindFlat is set // according to spec. continue } // This may be a legacy bitfield, try to fix it up. data, ok := d.legacyBitfields[bm.Type] if ok { // Bingo! m.Offset += data[0] m.BitfieldSize = data[1] continue } } return members, nil } idx := int(id - d.firstTypeID) if idx >= len(d.offsets) { return nil, fmt.Errorf("type id %v: %w", id, ErrNotFound) } offset := d.offsets[idx] if offset >= len(d.raw) { return nil, fmt.Errorf("offset out of bounds") } var ( header btfType bInt btfInt bArr btfArray bVariable btfVariable bDeclTag btfDeclTag pos = d.raw[offset:] ) { if n, err := unmarshalBtfType(&header, pos, d.byteOrder); err != nil { return nil, fmt.Errorf("can't unmarshal type info for id %v: %v", id, err) } else { pos = pos[n:] } name, err := d.strings.Lookup(header.NameOff) if err != nil { return nil, fmt.Errorf("get name for type id %d: %w", id, err) } switch header.Kind() { case kindInt: size := header.Size() if _, err := unmarshalBtfInt(&bInt, pos, d.byteOrder); err != nil { return nil, fmt.Errorf("can't unmarshal btfInt, id: %d: %w", id, err) } if bInt.Offset() > 0 || bInt.Bits().Bytes() != size { d.legacyBitfields[id] = [2]Bits{bInt.Offset(), bInt.Bits()} } typ = &Int{name, header.Size(), bInt.Encoding()} d.types[id] = typ case kindPointer: ptr := &Pointer{nil} d.types[id] = ptr fixup(header.Type(), &ptr.Target) typ = ptr case kindArray: if _, err := unmarshalBtfArray(&bArr, pos, d.byteOrder); err != nil { return nil, fmt.Errorf("can't unmarshal btfArray, id: %d: %w", id, err) } arr := &Array{nil, nil, bArr.Nelems} d.types[id] = arr fixup(bArr.IndexType, &arr.Index) fixup(bArr.Type, &arr.Type) typ = arr case kindStruct: str := &Struct{name, header.Size(), nil, nil} d.types[id] = str typ = str str.Members, err = convertMembers(&header, pos) if err != nil { return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err) } case kindUnion: uni := &Union{name, header.Size(), nil, nil} d.types[id] = uni typ = uni uni.Members, err = convertMembers(&header, pos) if err != nil { return nil, fmt.Errorf("union %s (id %d): %w", name, id, err) } case kindEnum: enum := &Enum{name, header.Size(), header.Signed(), nil} d.types[id] = enum typ = enum var be btfEnum enum.Values = make([]EnumValue, 0, header.Vlen()) for i := range header.Vlen() { n, err := unmarshalBtfEnum(&be, pos, d.byteOrder) if err != nil { return nil, fmt.Errorf("unmarshal btfEnum %d, id: %d: %w", i, id, err) } pos = pos[n:] name, err := d.strings.Lookup(be.NameOff) if err != nil { return nil, fmt.Errorf("get name for enum value %d: %s", i, err) } value := uint64(be.Val) if enum.Signed { // Sign extend values to 64 bit. value = uint64(int32(be.Val)) } enum.Values = append(enum.Values, EnumValue{name, value}) } case kindForward: typ = &Fwd{name, header.FwdKind()} d.types[id] = typ case kindTypedef: typedef := &Typedef{name, nil, nil} d.types[id] = typedef fixup(header.Type(), &typedef.Type) typ = typedef case kindVolatile: volatile := &Volatile{nil} d.types[id] = volatile fixup(header.Type(), &volatile.Type) typ = volatile case kindConst: cnst := &Const{nil} d.types[id] = cnst fixup(header.Type(), &cnst.Type) typ = cnst case kindRestrict: restrict := &Restrict{nil} d.types[id] = restrict fixup(header.Type(), &restrict.Type) typ = restrict case kindFunc: fn := &Func{name, nil, header.Linkage(), nil, nil} d.types[id] = fn fixup(header.Type(), &fn.Type) typ = fn case kindFuncProto: fp := &FuncProto{} d.types[id] = fp params := make([]FuncParam, 0, header.Vlen()) var bParam btfParam for i := range header.Vlen() { n, err := unmarshalBtfParam(&bParam, pos, d.byteOrder) if err != nil { return nil, fmt.Errorf("can't unmarshal btfParam %d, id: %d: %w", i, id, err) } pos = pos[n:] name, err := d.strings.Lookup(bParam.NameOff) if err != nil { return nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err) } param := FuncParam{Name: name} fixup(bParam.Type, ¶m.Type) params = append(params, param) } fixup(header.Type(), &fp.Return) fp.Params = params typ = fp case kindVar: if _, err := unmarshalBtfVariable(&bVariable, pos, d.byteOrder); err != nil { return nil, fmt.Errorf("can't read btfVariable, id: %d: %w", id, err) } v := &Var{name, nil, VarLinkage(bVariable.Linkage), nil} d.types[id] = v fixup(header.Type(), &v.Type) typ = v case kindDatasec: ds := &Datasec{name, header.Size(), nil} d.types[id] = ds vlen := header.Vlen() vars := make([]VarSecinfo, 0, vlen) var bSecInfo btfVarSecinfo for i := 0; i < vlen; i++ { n, err := unmarshalBtfVarSecInfo(&bSecInfo, pos, d.byteOrder) if err != nil { return nil, fmt.Errorf("can't unmarshal btfVarSecinfo %d, id: %d: %w", i, id, err) } pos = pos[n:] vs := VarSecinfo{ Offset: bSecInfo.Offset, Size: bSecInfo.Size, } fixup(bSecInfo.Type, &vs.Type) vars = append(vars, vs) } ds.Vars = vars typ = ds case kindFloat: typ = &Float{name, header.Size()} d.types[id] = typ case kindDeclTag: if _, err := unmarshalBtfDeclTag(&bDeclTag, pos, d.byteOrder); err != nil { return nil, fmt.Errorf("can't read btfDeclTag, id: %d: %w", id, err) } btfIndex := bDeclTag.ComponentIdx if uint64(btfIndex) > math.MaxInt { return nil, fmt.Errorf("type id %d: index exceeds int", id) } dt := &declTag{nil, name, int(int32(btfIndex))} d.types[id] = dt fixup(header.Type(), &dt.Type) typ = dt case kindTypeTag: tt := &TypeTag{nil, name} d.types[id] = tt fixup(header.Type(), &tt.Type) typ = tt case kindEnum64: enum := &Enum{name, header.Size(), header.Signed(), nil} d.types[id] = enum typ = enum enum.Values = make([]EnumValue, 0, header.Vlen()) var bEnum64 btfEnum64 for i := range header.Vlen() { n, err := unmarshalBtfEnum64(&bEnum64, pos, d.byteOrder) if err != nil { return nil, fmt.Errorf("can't unmarshal btfEnum64 %d, id: %d: %w", i, id, err) } pos = pos[n:] name, err := d.strings.Lookup(bEnum64.NameOff) if err != nil { return nil, fmt.Errorf("get name for enum64 value %d: %s", i, err) } value := (uint64(bEnum64.ValHi32) << 32) | uint64(bEnum64.ValLo32) enum.Values = append(enum.Values, EnumValue{name, value}) } default: return nil, fmt.Errorf("type id %d: unknown kind: %v", id, header.Kind()) } } for _, tagID := range d.declTags[id] { dtType, err := d.inflateType(tagID) if err != nil { return nil, err } dt, ok := dtType.(*declTag) if !ok { return nil, fmt.Errorf("type id %v: not a declTag", tagID) } switch t := typ.(type) { case *Var: if dt.Index != -1 { return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index) } t.Tags = append(t.Tags, dt.Value) case *Typedef: if dt.Index != -1 { return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index) } t.Tags = append(t.Tags, dt.Value) case composite: if dt.Index >= 0 { members := t.members() if dt.Index >= len(members) { return nil, fmt.Errorf("type %s: component idx %d exceeds members of %s", dt, dt.Index, t) } members[dt.Index].Tags = append(members[dt.Index].Tags, dt.Value) } else if dt.Index == -1 { switch t2 := t.(type) { case *Struct: t2.Tags = append(t2.Tags, dt.Value) case *Union: t2.Tags = append(t2.Tags, dt.Value) } } else { return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t) } case *Func: fp, ok := t.Type.(*FuncProto) if !ok { return nil, fmt.Errorf("type %s: %s is not a FuncProto", dt, t.Type) } // Ensure the number of argument tag lists equals the number of arguments if len(t.ParamTags) == 0 { t.ParamTags = make([][]string, len(fp.Params)) } if dt.Index >= 0 { if dt.Index >= len(fp.Params) { return nil, fmt.Errorf("type %s: component idx %d exceeds params of %s", dt, dt.Index, t) } t.ParamTags[dt.Index] = append(t.ParamTags[dt.Index], dt.Value) } else if dt.Index == -1 { t.Tags = append(t.Tags, dt.Value) } else { return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t) } default: return nil, fmt.Errorf("type %s: decl tag for type %s is not supported", dt, t) } } return typ, nil } // An index from string to TypeID. // // Fuzzy because it may return false positive matches. type fuzzyStringIndex struct { seed maphash.Seed entries []fuzzyStringIndexEntry } func newFuzzyStringIndex(capacity int) *fuzzyStringIndex { return &fuzzyStringIndex{ maphash.MakeSeed(), make([]fuzzyStringIndexEntry, 0, capacity), } } // Add a string to the index. // // Calling the method with identical arguments will create duplicate entries. func (idx *fuzzyStringIndex) Add(name []byte, id TypeID) { hash := uint32(maphash.Bytes(idx.seed, name)) idx.entries = append(idx.entries, newFuzzyStringIndexEntry(hash, id)) } // Build the index. // // Must be called after [Add] and before [Match]. func (idx *fuzzyStringIndex) Build() { slices.Sort(idx.entries) } // Find TypeIDs which may match the name. // // May return false positives, but is guaranteed to not have false negatives. // // You must call [Build] at least once before calling this method. func (idx *fuzzyStringIndex) Find(name string) iter.Seq[TypeID] { return func(yield func(TypeID) bool) { hash := uint32(maphash.String(idx.seed, name)) // We match only on the first 32 bits here, so ignore found. i, _ := slices.BinarySearch(idx.entries, fuzzyStringIndexEntry(hash)<<32) for i := i; i < len(idx.entries); i++ { if idx.entries[i].hash() != hash { break } if !yield(idx.entries[i].id()) { return } } } } // Tuple mapping the hash of an essential name to a type. // // Encoded in an uint64 so that it implements cmp.Ordered. type fuzzyStringIndexEntry uint64 func newFuzzyStringIndexEntry(hash uint32, id TypeID) fuzzyStringIndexEntry { return fuzzyStringIndexEntry(hash)<<32 | fuzzyStringIndexEntry(id) } func (e fuzzyStringIndexEntry) hash() uint32 { return uint32(e >> 32) } func (e fuzzyStringIndexEntry) id() TypeID { return TypeID(e) } golang-github-cilium-ebpf-0.21.0+ds1/btf/unmarshal_test.go000066400000000000000000000014231520243672000233320ustar00rootroot00000000000000package btf import ( "iter" "math" "testing" "github.com/go-quicktest/qt" ) func TestFuzzyStringIndex(t *testing.T) { idx := newFuzzyStringIndex(10) count := testing.AllocsPerRun(1, func() { idx.Add([]byte("foo"), 1) }) qt.Assert(t, qt.Equals(count, 0)) idx.entries = idx.entries[:0] idx.Add([]byte("foo"), 1) idx.Add([]byte("bar"), 2) idx.Add([]byte("baz"), 3) idx.Build() all := func(it iter.Seq[TypeID]) (ids []TypeID) { for id := range it { ids = append(ids, id) } return } qt.Assert(t, qt.SliceContains(all(idx.Find("foo")), 1)) qt.Assert(t, qt.SliceContains(all(idx.Find("bar")), 2)) qt.Assert(t, qt.SliceContains(all(idx.Find("baz")), 3)) qt.Assert(t, qt.IsTrue(newFuzzyStringIndexEntry(0, math.MaxUint32) < newFuzzyStringIndexEntry(1, 0))) } golang-github-cilium-ebpf-0.21.0+ds1/btf/workarounds.go000066400000000000000000000012231520243672000226550ustar00rootroot00000000000000package btf // datasecResolveWorkaround ensures that certain vars in a Datasec are added // to a Spec before the Datasec. This avoids a bug in kernel BTF validation. // // See https://lore.kernel.org/bpf/20230302123440.1193507-1-lmb@isovalent.com/ func datasecResolveWorkaround(b *Builder, ds *Datasec) error { for _, vsi := range ds.Vars { v, ok := vsi.Type.(*Var) if !ok { continue } switch v.Type.(type) { case *Typedef, *Volatile, *Const, *Restrict, *TypeTag: // NB: We must never call Add on a Datasec, otherwise we risk // infinite recursion. _, err := b.Add(v.Type) if err != nil { return err } } } return nil } golang-github-cilium-ebpf-0.21.0+ds1/btf/workarounds_test.go000066400000000000000000000027351520243672000237250ustar00rootroot00000000000000package btf import ( "errors" "fmt" "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" ) func TestDatasecResolveWorkaround(t *testing.T) { testutils.SkipOnOldKernel(t, "5.2", "BTF_KIND_DATASEC") i := &Int{Size: 1} for _, typ := range []Type{ &Typedef{"foo", i, nil}, &Volatile{i}, &Const{i}, &Restrict{i}, &TypeTag{i, "foo"}, } { t.Run(fmt.Sprint(typ), func(t *testing.T) { if _, ok := typ.(*TypeTag); ok { testutils.SkipOnOldKernel(t, "5.17", "BTF_KIND_TYPE_TAG") } ds := &Datasec{ Name: "a", Size: 2, Vars: []VarSecinfo{ { Size: 1, Offset: 0, // struct, union, pointer, array will trigger the bug. Type: &Var{Name: "a", Type: &Pointer{i}}, }, { Size: 1, Offset: 1, Type: &Var{ Name: "b", Type: typ, }, }, }, } b, err := NewBuilder([]Type{ds}, nil) if err != nil { t.Fatal(err) } h, err := NewHandle(b) testutils.SkipIfNotSupportedOnOS(t, err) var ve *internal.VerifierError if errors.As(err, &ve) { t.Fatalf("%+v\n", ve) } if err != nil { t.Fatal(err) } h.Close() }) } } func TestEmptyBTFWithStringTableWorkaround(t *testing.T) { var b Builder _, err := b.addString("foo") qt.Assert(t, qt.IsNil(err)) h, err := NewHandle(&b) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(h.Close())) } golang-github-cilium-ebpf-0.21.0+ds1/cmd/000077500000000000000000000000001520243672000177425ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/000077500000000000000000000000001520243672000211215ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/README.md000066400000000000000000000026671520243672000224130ustar00rootroot00000000000000bpf2go === `bpf2go` compiles a C source file into eBPF bytecode and then emits a Go file containing the eBPF. The goal is to avoid loading the eBPF from disk at runtime and to minimise the amount of manual work required to interact with eBPF programs. It takes inspiration from `bpftool gen skeleton`. Add `bpf2go` as a tool dependency in your project's Go module: go get -tool github.com/cilium/ebpf/cmd/bpf2go Invoke the tool using go generate: //go:generate go tool bpf2go foo path/to/src.c -- -I/path/to/include This will emit `foo_bpfel.go` and `foo_bpfeb.go`, with types using `foo` as a stem. The two files contain compiled BPF for little and big endian systems, respectively. ## Environment Variables You can use environment variables to affect all bpf2go invocations across a project, e.g. to set specific C flags: BPF2GO_CFLAGS="-O2 -g -Wall -Werror $(CFLAGS)" go generate ./... Alternatively, by exporting `$BPF2GO_CFLAGS` from your build system, you can control all builds from a single location. Most bpf2go arguments can be controlled this way. See `bpf2go -h` for an up-to-date list. ## Generated types `bpf2go` generates Go types for all map keys and values by default. You can disable this behaviour using `-no-global-types`. You can add to the set of types by specifying `-type foo` for each type you'd like to generate. ## Examples See [examples/kprobe](../../examples/kprobe/main.go) for a fully worked out example. golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/doc.go000066400000000000000000000001761520243672000222210ustar00rootroot00000000000000//go:build !windows // Program bpf2go embeds eBPF in Go. // // Please see the README for details how to use it. package main golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/flags.go000066400000000000000000000013311520243672000225420ustar00rootroot00000000000000//go:build !windows package main import ( "flag" "go/build/constraint" ) // buildTags is a comma-separated list of build tags. // // This follows the pre-Go 1.17 syntax and is kept for compatibility reasons. type buildTags struct { Expr constraint.Expr } var _ flag.Value = (*buildTags)(nil) func (bt *buildTags) String() string { if bt.Expr == nil { return "" } return (bt.Expr).String() } func (bt *buildTags) Set(value string) error { ct, err := constraint.Parse("// +build " + value) if err != nil { return err } bt.Expr = ct return nil } func andConstraints(x, y constraint.Expr) constraint.Expr { if x == nil { return y } if y == nil { return x } return &constraint.AndExpr{X: x, Y: y} } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/000077500000000000000000000000001520243672000216725ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/compile.go000066400000000000000000000053401520243672000236530ustar00rootroot00000000000000//go:build !windows package gen import ( "fmt" "os" "os/exec" "path/filepath" "strings" ) type CompileArgs struct { // Which compiler to use. CC string // Command used to strip DWARF from the ELF. Strip string // Flags to pass to the compiler. This may contain positional arguments as well. Flags []string // Absolute working directory Workdir string // Absolute input file name Source string // Absolute output file name Dest string // Target to compile for, defaults to compiling generic BPF in host endianness. Target Target DisableStripping bool } func insertDefaultFlags(flags []string) []string { // Default cflags that can be overridden by the user. overrideFlags := []string{ // Code needs to be optimized, otherwise the verifier will often fail // to understand it. "-O2", // Clang defaults to mcpu=probe which checks the kernel that we are // compiling on. This isn't appropriate for ahead of time // compiled code so force the most compatible version. "-mcpu=v1", } insert := 0 // Find the first non-positional argument to support CC commands with // multiple components. E.g.: BPF2GO_CC="ccache clang" ... for ; insert < len(flags); insert++ { if strings.HasPrefix(flags[insert], "-") { break } } result := append([]string(nil), flags[:insert]...) result = append(result, overrideFlags...) result = append(result, flags[insert:]...) return result } // Compile C to a BPF ELF file. func Compile(args CompileArgs) error { cmd := exec.Command(args.CC, insertDefaultFlags(args.Flags)...) cmd.Stderr = os.Stderr inputDir := filepath.Dir(args.Source) relInputDir, err := filepath.Rel(args.Workdir, inputDir) if err != nil { return err } target := args.Target if target == (Target{}) { target.clang = "bpf" } // C flags that can't be overridden. if linux := target.linux; linux != "" { cmd.Args = append(cmd.Args, "-D__TARGET_ARCH_"+linux) } cmd.Args = append(cmd.Args, "-Wunused-command-line-argument", "-target", target.clang, "-c", args.Source, "-o", args.Dest, // Don't include clang version "-fno-ident", // Don't output inputDir into debug info "-fdebug-prefix-map="+inputDir+"="+relInputDir, "-fdebug-compilation-dir", ".", // We always want BTF to be generated, so enforce debug symbols "-g", fmt.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\""), ) cmd.Dir = args.Workdir if err := cmd.Run(); err != nil { return err } if args.DisableStripping { return nil } cmd = exec.Command(args.Strip, "-g", args.Dest) cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("strip %s: %w", args.Dest, err) } return nil } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/compile_test.go000066400000000000000000000046721520243672000247210ustar00rootroot00000000000000//go:build !windows package gen import ( "bytes" "os" "path/filepath" "testing" "github.com/cilium/ebpf/internal/testutils" ) const minimalSocketFilter = `__attribute__((section("socket"), used)) int main() { return 0; }` func TestCompile(t *testing.T) { if testing.Short() { t.SkipNow() } dir := t.TempDir() mustWriteFile(t, dir, "test.c", minimalSocketFilter) err := Compile(CompileArgs{ CC: testutils.ClangBin(t), DisableStripping: true, Workdir: dir, Source: filepath.Join(dir, "test.c"), Dest: filepath.Join(dir, "test.o"), }) if err != nil { t.Fatal("Can't compile:", err) } stat, err := os.Stat(filepath.Join(dir, "test.o")) if err != nil { t.Fatal("Can't stat output:", err) } if stat.Size() == 0 { t.Error("Compilation creates an empty file") } } func TestReproducibleCompile(t *testing.T) { if testing.Short() { t.SkipNow() } clangBin := testutils.ClangBin(t) dir := t.TempDir() mustWriteFile(t, dir, "test.c", minimalSocketFilter) err := Compile(CompileArgs{ CC: clangBin, DisableStripping: true, Workdir: dir, Source: filepath.Join(dir, "test.c"), Dest: filepath.Join(dir, "a.o"), }) if err != nil { t.Fatal("Can't compile:", err) } err = Compile(CompileArgs{ CC: clangBin, DisableStripping: true, Workdir: dir, Source: filepath.Join(dir, "test.c"), Dest: filepath.Join(dir, "b.o"), }) if err != nil { t.Fatal("Can't compile:", err) } aBytes, err := os.ReadFile(filepath.Join(dir, "a.o")) if err != nil { t.Fatal(err) } bBytes, err := os.ReadFile(filepath.Join(dir, "b.o")) if err != nil { t.Fatal(err) } if !bytes.Equal(aBytes, bBytes) { t.Error("Compiling the same file twice doesn't give the same result") } } func TestTriggerMissingTarget(t *testing.T) { if testing.Short() { t.SkipNow() } dir := t.TempDir() mustWriteFile(t, dir, "test.c", `_Pragma(__BPF_TARGET_MISSING);`) err := Compile(CompileArgs{ CC: testutils.ClangBin(t), Workdir: dir, Source: filepath.Join(dir, "test.c"), Dest: filepath.Join(dir, "a.o"), }) if err == nil { t.Fatal("No error when compiling __BPF_TARGET_MISSING") } } func mustWriteFile(tb testing.TB, dir, name, contents string) { tb.Helper() tmpFile := filepath.Join(dir, name) if err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil { tb.Fatal(err) } } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/doc.go000066400000000000000000000001321520243672000227620ustar00rootroot00000000000000// Package gen contains utilities to generate Go bindings for eBPF ELF files. package gen golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/output.go000066400000000000000000000127311520243672000235650ustar00rootroot00000000000000//go:build !windows package gen import ( "bytes" _ "embed" "fmt" "go/build/constraint" "go/token" "io" "sort" "strings" "text/template" "unicode" "unicode/utf8" "github.com/cilium/ebpf/btf" b2gInt "github.com/cilium/ebpf/cmd/bpf2go/internal" "github.com/cilium/ebpf/internal" ) //go:embed output.tpl var commonRaw string var commonTemplate = template.Must(template.New("common").Parse(commonRaw)) type templateName string func (n templateName) maybeExport(str string) string { if token.IsExported(string(n)) { return toUpperFirst(str) } return str } func (n templateName) Bytes() string { return "_" + toUpperFirst(string(n)) + "Bytes" } func (n templateName) Specs() string { return string(n) + "Specs" } func (n templateName) ProgramSpecs() string { return string(n) + "ProgramSpecs" } func (n templateName) MapSpecs() string { return string(n) + "MapSpecs" } func (n templateName) VariableSpecs() string { return string(n) + "VariableSpecs" } func (n templateName) Load() string { return n.maybeExport("load" + toUpperFirst(string(n))) } func (n templateName) LoadObjects() string { return n.maybeExport("load" + toUpperFirst(string(n)) + "Objects") } func (n templateName) Objects() string { return string(n) + "Objects" } func (n templateName) Maps() string { return string(n) + "Maps" } func (n templateName) Variables() string { return string(n) + "Variables" } func (n templateName) Programs() string { return string(n) + "Programs" } func (n templateName) CloseHelper() string { return "_" + toUpperFirst(string(n)) + "Close" } type GenerateArgs struct { // Package of the resulting file. Package string // The prefix of all names declared at the top-level. Stem string // Build Constraints included in the resulting file. Constraints constraint.Expr // Maps to be emitted. Maps []string // Variables to be emitted. Variables []string // Programs to be emitted. Programs []string // Types to be emitted. Types []btf.Type // Filename of the object to embed. ObjectFile string // Output to write template to. Output io.Writer // Function which transforms the input into a valid go identifier. Uses the default behaviour if nil Identifier func(string) string } // Generate bindings for a BPF ELF file. func Generate(args GenerateArgs) error { if args.Identifier == nil { args.Identifier = internal.Identifier } if !token.IsIdentifier(args.Stem) { return fmt.Errorf("%q is not a valid identifier", args.Stem) } if strings.ContainsAny(args.ObjectFile, "\n") { // Prevent injecting newlines into the template. return fmt.Errorf("file %q contains an invalid character", args.ObjectFile) } for _, typ := range args.Types { if _, ok := btf.As[*btf.Datasec](typ); ok { // Avoid emitting .rodata, .bss, etc. for now. We might want to // name these types differently, etc. return fmt.Errorf("can't output btf.Datasec: %s", typ) } } maps := make(map[string]string) for _, name := range args.Maps { maps[name] = args.Identifier(name) } variables := make(map[string]string) for _, name := range args.Variables { variables[name] = args.Identifier(name) } programs := make(map[string]string) for _, name := range args.Programs { programs[name] = args.Identifier(name) } typeNames := make(map[btf.Type]string) for _, typ := range args.Types { // NB: This also deduplicates types. typeNames[typ] = args.Stem + args.Identifier(typ.TypeName()) } // Ensure we don't have conflicting names and generate a sorted list of // named types so that the output is stable. types, err := sortTypes(typeNames) if err != nil { return err } gf := &btf.GoFormatter{ Names: typeNames, Identifier: args.Identifier, } var typeDecls []string needsStructsPkg := false for _, typ := range types { name := typeNames[typ] decl, err := gf.TypeDeclaration(name, typ) if err != nil { return fmt.Errorf("generating %s: %w", name, err) } _, ok := btf.As[*btf.Struct](typ) needsStructsPkg = needsStructsPkg || ok typeDecls = append(typeDecls, decl) } ctx := struct { Module string Package string Constraints constraint.Expr Name templateName Maps map[string]string Variables map[string]string Programs map[string]string TypeDeclarations []string File string NeedsStructsPkg bool }{ b2gInt.CurrentModule, args.Package, args.Constraints, templateName(args.Stem), maps, variables, programs, typeDecls, args.ObjectFile, needsStructsPkg, } var buf bytes.Buffer if err := commonTemplate.Execute(&buf, &ctx); err != nil { return fmt.Errorf("can't generate types: %s", err) } return internal.WriteFormatted(buf.Bytes(), args.Output) } // sortTypes returns a list of types sorted by their (generated) Go type name. // // Duplicate Go type names are rejected. func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) { var types []btf.Type var names []string for typ, name := range typeNames { i := sort.SearchStrings(names, name) if i >= len(names) { types = append(types, typ) names = append(names, name) continue } if names[i] == name { return nil, fmt.Errorf("type name %q is used multiple times", name) } types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...) names = append(names[:i], append([]string{name}, names[i:]...)...) } return types, nil } func toUpperFirst(str string) string { first, n := utf8.DecodeRuneInString(str) return string(unicode.ToUpper(first)) + str[n:] } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/output.tpl000066400000000000000000000077531520243672000237670ustar00rootroot00000000000000// Code generated by bpf2go; DO NOT EDIT. {{ with .Constraints }}//go:build {{ . }}{{ end }} package {{ .Package }} import ( "bytes" _ "embed" "fmt" "io" {{- if .NeedsStructsPkg }} "structs" {{- end }} "{{ .Module }}" ) {{- if .TypeDeclarations }} {{- range $type := .TypeDeclarations }} {{ $type }} {{ end }} {{- end }} // {{ .Name.Load }} returns the embedded CollectionSpec for {{ .Name }}. func {{ .Name.Load }}() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader({{ .Name.Bytes }}) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load {{ .Name }}: %w", err) } return spec, err } // {{ .Name.LoadObjects }} loads {{ .Name }} and converts it into a struct. // // The following types are suitable as obj argument: // // *{{ .Name.Objects }} // *{{ .Name.Programs }} // *{{ .Name.Maps }} // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func {{ .Name.LoadObjects }}(obj interface{}, opts *ebpf.CollectionOptions) (error) { spec, err := {{ .Name.Load }}() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // {{ .Name.Specs }} contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type {{ .Name.Specs }} struct { {{ .Name.ProgramSpecs }} {{ .Name.MapSpecs }} {{ .Name.VariableSpecs }} } // {{ .Name.ProgramSpecs }} contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type {{ .Name.ProgramSpecs }} struct { {{- range $name, $id := .Programs }} {{ $id }} *ebpf.ProgramSpec `ebpf:"{{ $name }}"` {{- end }} } // {{ .Name.MapSpecs }} contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type {{ .Name.MapSpecs }} struct { {{- range $name, $id := .Maps }} {{ $id }} *ebpf.MapSpec `ebpf:"{{ $name }}"` {{- end }} } // {{ .Name.VariableSpecs }} contains global variables before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type {{ .Name.VariableSpecs }} struct { {{- range $name, $id := .Variables }} {{ $id }} *ebpf.VariableSpec `ebpf:"{{ $name }}"` {{- end }} } // {{ .Name.Objects }} contains all objects after they have been loaded into the kernel. // // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. type {{ .Name.Objects }} struct { {{ .Name.Programs }} {{ .Name.Maps }} {{ .Name.Variables }} } func (o *{{ .Name.Objects }}) Close() error { return {{ .Name.CloseHelper }}( &o.{{ .Name.Programs }}, &o.{{ .Name.Maps }}, ) } // {{ .Name.Maps }} contains all maps after they have been loaded into the kernel. // // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. type {{ .Name.Maps }} struct { {{- range $name, $id := .Maps }} {{ $id }} *ebpf.Map `ebpf:"{{ $name }}"` {{- end }} } func (m *{{ .Name.Maps }}) Close() error { return {{ .Name.CloseHelper }}( {{- range $id := .Maps }} m.{{ $id }}, {{- end }} ) } // {{ .Name.Variables }} contains all global variables after they have been loaded into the kernel. // // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. type {{ .Name.Variables }} struct { {{- range $name, $id := .Variables }} {{ $id }} *ebpf.Variable `ebpf:"{{ $name }}"` {{- end }} } // {{ .Name.Programs }} contains all programs after they have been loaded into the kernel. // // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign. type {{ .Name.Programs }} struct { {{- range $name, $id := .Programs }} {{ $id }} *ebpf.Program `ebpf:"{{ $name }}"` {{- end }} } func (p *{{ .Name.Programs }}) Close() error { return {{ .Name.CloseHelper }}( {{- range $id := .Programs }} p.{{ $id }}, {{- end }} ) } func {{ .Name.CloseHelper }}(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. //go:embed {{ .File }} var {{ .Name.Bytes }} []byte golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/output_test.go000066400000000000000000000074071520243672000246300ustar00rootroot00000000000000//go:build !windows package gen import ( "bytes" "fmt" "strings" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/cmd/bpf2go/internal" ) func TestOrderTypes(t *testing.T) { a := &btf.Int{} b := &btf.Int{} c := &btf.Int{} for _, test := range []struct { name string in map[btf.Type]string out []btf.Type }{ { "order", map[btf.Type]string{ a: "foo", b: "bar", c: "baz", }, []btf.Type{b, c, a}, }, } { t.Run(test.name, func(t *testing.T) { result, err := sortTypes(test.in) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(len(result), len(test.out))) for i, o := range test.out { if result[i] != o { t.Fatalf("Index %d: expected %p got %p", i, o, result[i]) } } }) } for _, test := range []struct { name string in map[btf.Type]string }{ { "duplicate names", map[btf.Type]string{ a: "foo", b: "foo", }, }, } { t.Run(test.name, func(t *testing.T) { result, err := sortTypes(test.in) qt.Assert(t, qt.IsNotNil(err)) qt.Assert(t, qt.IsNil(result)) }) } } func TestPackageImport(t *testing.T) { var buf bytes.Buffer err := Generate(GenerateArgs{ Package: "foo", Stem: "bar", ObjectFile: "frob.o", Output: &buf, }) qt.Assert(t, qt.IsNil(err)) // NB: It'd be great to test that this is the case for callers outside of // this module, but that is kind of tricky. qt.Assert(t, qt.StringContains(buf.String(), fmt.Sprintf(`"%s"`, internal.CurrentModule))) } func TestCustomIdentifier(t *testing.T) { var buf bytes.Buffer args := GenerateArgs{ Package: "foo", Stem: "bar", ObjectFile: "frob.o", Output: &buf, Programs: []string{"do_thing"}, Identifier: strings.ToUpper, } err := Generate(args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.StringContains(buf.String(), "DO_THING")) } func TestObjects(t *testing.T) { var buf bytes.Buffer args := GenerateArgs{ Package: "foo", Stem: "bar", Maps: []string{"map1"}, Variables: []string{"var_1"}, Programs: []string{"prog_foo_1"}, Output: &buf, } err := Generate(args) qt.Assert(t, qt.IsNil(err)) str := buf.String() qt.Assert(t, qt.StringContains(str, "Map1 *ebpf.MapSpec `ebpf:\"map1\"`")) qt.Assert(t, qt.StringContains(str, "Var1 *ebpf.VariableSpec `ebpf:\"var_1\"`")) qt.Assert(t, qt.StringContains(str, "ProgFoo1 *ebpf.ProgramSpec `ebpf:\"prog_foo_1\"`")) qt.Assert(t, qt.StringContains(str, "Map1 *ebpf.Map `ebpf:\"map1\"`")) qt.Assert(t, qt.StringContains(str, "Var1 *ebpf.Variable `ebpf:\"var_1\"`")) qt.Assert(t, qt.StringContains(str, "ProgFoo1 *ebpf.Program `ebpf:\"prog_foo_1\"`")) } func TestGenerateStructTypes(t *testing.T) { ts := &btf.Struct{ Name: "test_struct", Size: 8, Members: []btf.Member{ { Name: "field1", Type: &btf.Int{Size: 8, Encoding: btf.Unsigned}, Offset: 0, }, }, } td := &btf.Typedef{ Name: "test_typedef", Type: ts, } tests := []struct { name string types []btf.Type expected string }{ { name: "simple struct", types: []btf.Type{ts}, expected: "type stemTestStruct struct {\n\t_ structs.HostLayout\n\tField1 uint64\n}", }, { name: "typedef struct", types: []btf.Type{td}, expected: "type stemTestTypedef struct {\n\t_ structs.HostLayout\n\tField1 uint64\n}", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var buf bytes.Buffer err := Generate(GenerateArgs{ Package: "test", Stem: "stem", Types: tt.types, Output: &buf, Constraints: nil, }) qt.Assert(t, qt.IsNil(err)) str := buf.String() qt.Assert(t, qt.StringContains(str, tt.expected)) qt.Assert(t, qt.StringContains(str, "\"structs\"")) }) } } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/target.go000066400000000000000000000100261520243672000235060ustar00rootroot00000000000000//go:build !windows package gen import ( "errors" "fmt" "go/build/constraint" "maps" "runtime" "slices" ) var ErrInvalidTarget = errors.New("unsupported target") var targetsByGoArch = map[GoArch]Target{ "386": {"bpfel", "x86", ""}, "amd64": {"bpfel", "x86", ""}, "arm": {"bpfel", "arm", ""}, "arm64": {"bpfel", "arm64", ""}, "loong64": {"bpfel", "loongarch", ""}, "mips": {"bpfeb", "mips", ""}, "mipsle": {"bpfel", "", ""}, "mips64": {"bpfeb", "", ""}, "mips64le": {"bpfel", "", ""}, "ppc64": {"bpfeb", "powerpc", ""}, "ppc64le": {"bpfel", "powerpc", ""}, "riscv64": {"bpfel", "riscv", ""}, "s390x": {"bpfeb", "s390", ""}, "wasm": {"bpfel", "", "js"}, } type Target struct { // Clang arch string, used to define the clang -target flag, as per // "clang -print-targets". clang string // Linux arch string, used to define __TARGET_ARCH_xzy macros used by // https://github.com/libbpf/libbpf/blob/master/src/bpf_tracing.h linux string // GOOS override for use during tests. goos string } // TargetsByGoArch returns all supported targets. func TargetsByGoArch() map[GoArch]Target { return maps.Clone(targetsByGoArch) } // IsGeneric returns true if the target will compile to generic BPF. func (tgt *Target) IsGeneric() bool { return tgt.linux == "" } // Suffix returns a a string suitable for appending to a file name to // identify the target. func (tgt *Target) Suffix() string { // The output filename must not match any of the following patterns: // // *_GOOS // *_GOARCH // *_GOOS_GOARCH // // Otherwise it is interpreted as a build constraint by the Go toolchain. stem := tgt.clang if tgt.linux != "" { stem = fmt.Sprintf("%s_%s", tgt.linux, tgt.clang) } return stem } // ObsoleteSuffix returns an obsolete suffix for a subset of targets. // // It's used to work around an old bug and should not be used in new code. func (tgt *Target) ObsoleteSuffix() string { if tgt.linux == "" { return "" } return fmt.Sprintf("%s_%s", tgt.clang, tgt.linux) } // GoArch is a Go arch string. // // See https://go.dev/doc/install/source#environment for valid GOARCHes when // GOOS=linux. type GoArch string type GoArches []GoArch // Constraints is satisfied when GOARCH is any of the arches. func (arches GoArches) Constraint() constraint.Expr { var archConstraint constraint.Expr for _, goarch := range arches { tag := &constraint.TagExpr{Tag: string(goarch)} archConstraint = orConstraints(archConstraint, tag) } return archConstraint } // FindTarget turns a list of identifiers into targets and their respective // GoArches. // // The following are valid identifiers: // // - bpf: compile generic BPF for host endianness // - bpfel: compile generic BPF for little endian // - bpfeb: compile generic BPF for big endian // - native: compile BPF for host target // - $GOARCH: compile BPF for $GOARCH target // // Generic BPF can run on any target goarch with the correct endianness, // but doesn't have access to some arch specific tracing functionality. func FindTarget(id string) (Target, GoArches, error) { switch id { case "bpf", "bpfel", "bpfeb": var goarches []GoArch for arch, archTarget := range targetsByGoArch { if archTarget.clang == id { // Include tags for all goarches that have the same endianness. goarches = append(goarches, arch) } } slices.Sort(goarches) return Target{id, "", ""}, goarches, nil case "native": id = runtime.GOARCH fallthrough default: archTarget, ok := targetsByGoArch[GoArch(id)] if !ok || archTarget.linux == "" { return Target{}, nil, fmt.Errorf("%q: %w", id, ErrInvalidTarget) } var goarches []GoArch for goarch, lt := range targetsByGoArch { if lt == archTarget { // Include tags for all goarches that have the same // target. goarches = append(goarches, goarch) } } slices.Sort(goarches) return archTarget, goarches, nil } } func orConstraints(x, y constraint.Expr) constraint.Expr { if x == nil { return y } if y == nil { return x } return &constraint.OrExpr{X: x, Y: y} } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/target_test.go000066400000000000000000000065331520243672000245550ustar00rootroot00000000000000//go:build !windows package gen import ( "errors" "os/exec" "slices" "testing" "github.com/go-quicktest/qt" ) func TestCollectTargets(t *testing.T) { clangArches := make(map[string][]GoArch) linuxArchesLE := make(map[string][]GoArch) linuxArchesBE := make(map[string][]GoArch) for arch, archTarget := range targetsByGoArch { clangArches[archTarget.clang] = append(clangArches[archTarget.clang], arch) if archTarget.clang == "bpfel" { linuxArchesLE[archTarget.linux] = append(linuxArchesLE[archTarget.linux], arch) continue } linuxArchesBE[archTarget.linux] = append(linuxArchesBE[archTarget.linux], arch) } for i := range clangArches { slices.Sort(clangArches[i]) } for i := range linuxArchesLE { slices.Sort(linuxArchesLE[i]) } for i := range linuxArchesBE { slices.Sort(linuxArchesBE[i]) } nativeTarget, nativeArches, err := FindTarget("native") qt.Assert(t, qt.IsNil(err)) tests := []struct { short string target Target arches GoArches }{ { "bpf", Target{"bpf", "", ""}, nil, }, { "bpfel", Target{"bpfel", "", ""}, clangArches["bpfel"], }, { "bpfeb", Target{"bpfeb", "", ""}, clangArches["bpfeb"], }, { "amd64", Target{"bpfel", "x86", ""}, linuxArchesLE["x86"], }, { "386", Target{"bpfel", "x86", ""}, linuxArchesLE["x86"], }, { "ppc64", Target{"bpfeb", "powerpc", ""}, linuxArchesBE["powerpc"], }, { "native", nativeTarget, nativeArches, }, } for _, test := range tests { t.Run(test.short, func(t *testing.T) { target, arches, err := FindTarget(test.short) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(target, test.target)) qt.Assert(t, qt.DeepEquals(arches, test.arches)) }) } } func TestCollectTargetsErrors(t *testing.T) { tests := []struct { name string target string }{ {"unknown", "frood"}, {"no linux target", "mipsle"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, _, err := FindTarget(test.target) if err == nil { t.Fatal("Function did not return an error") } t.Log("Error message:", err) }) } } func TestGoarches(t *testing.T) { exe := goBin(t) for GoArch, tgt := range targetsByGoArch { t.Run(string(GoArch), func(t *testing.T) { goOS := "linux" if tgt.goos != "" { goOS = tgt.goos } goEnv := exec.Command(exe, "env") goEnv.Env = []string{"GOROOT=/", "GOOS=" + string(goOS), "GOARCH=" + string(GoArch)} output, err := goEnv.CombinedOutput() qt.Assert(t, qt.IsNil(err), qt.Commentf("go output is:\n%s", string(output))) }) } } func TestClangTargets(t *testing.T) { exe := goBin(t) clangTargets := map[string]struct{}{} for _, tgt := range targetsByGoArch { clangTargets[tgt.clang] = struct{}{} } for target := range clangTargets { for _, env := range []string{"GOOS", "GOARCH"} { env += "=" + target t.Run(env, func(t *testing.T) { goEnv := exec.Command(exe, "env") goEnv.Env = []string{"GOROOT=/", env} output, err := goEnv.CombinedOutput() t.Log("go output is:", string(output)) qt.Assert(t, qt.IsNotNil(err), qt.Commentf("No clang target should be a valid build constraint")) }) } } } func goBin(t *testing.T) string { t.Helper() exe, err := exec.LookPath("go") if errors.Is(err, exec.ErrNotFound) { t.Skip("go binary is not in PATH") } qt.Assert(t, qt.IsNil(err)) return exe } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/types.go000066400000000000000000000045341520243672000233730ustar00rootroot00000000000000//go:build !windows package gen import ( "cmp" "slices" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" ) // CollectGlobalTypes finds all types which are used in the global scope. // // This currently includes the types of variables, map keys and values. func CollectGlobalTypes(spec *ebpf.CollectionSpec) []btf.Type { var types []btf.Type types = collectMapTypes(types, spec.Maps) types = collectVariableTypes(types, spec.Variables) slices.SortStableFunc(types, func(a, b btf.Type) int { return cmp.Compare(a.TypeName(), b.TypeName()) }) return types } // collectMapTypes collects all types used by MapSpecs. func collectMapTypes(types []btf.Type, maps map[string]*ebpf.MapSpec) []btf.Type { for _, m := range maps { if m.Key != nil && m.Key.TypeName() != "" { types = addType(types, m.Key) } if m.Value != nil && m.Value.TypeName() != "" { types = addType(types, m.Value) } } return types } // collectVariableTypes collects all types used by VariableSpecs. func collectVariableTypes(types []btf.Type, vars map[string]*ebpf.VariableSpec) []btf.Type { for _, vs := range vars { types = addType(types, vs.Type.Type) } return types } // addType adds a type to types if not already present. Types that don't need to // be generated are not added to types. func addType(types []btf.Type, incoming btf.Type) []btf.Type { incoming = selectType(incoming) if incoming == nil { return types } // Strip only the qualifiers (not typedefs) from the incoming type. Retain // typedefs since they carry the name of the anonymous type they point to, // without which we can't generate a named Go type. incoming = btf.QualifiedType(incoming) if incoming.TypeName() == "" { return types } exists := func(existing btf.Type) bool { return existing.TypeName() == incoming.TypeName() } if !slices.ContainsFunc(types, exists) { types = append(types, incoming) } return types } func selectType(t btf.Type) btf.Type { // Obtain a concrete type with qualifiers and typedefs stripped. switch ut := btf.UnderlyingType(t).(type) { case *btf.Struct, *btf.Union, *btf.Enum: return t // Collect the array's element type. Note: qualifiers on array-type variables // typically appear after the array, e.g. a const volatile int[4] is actually // an array of const volatile ints. case *btf.Array: return selectType(ut.Type) } return nil } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/gen/types_test.go000066400000000000000000000020111520243672000244160ustar00rootroot00000000000000//go:build !windows package gen import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" "github.com/google/go-cmp/cmp" ) func mustAnyTypeByName(t *testing.T, spec *ebpf.CollectionSpec, name string) btf.Type { t.Helper() typ, err := spec.Types.AnyTypeByName(name) qt.Assert(t, qt.IsNil(err)) return typ } func TestCollectGlobalTypes(t *testing.T) { spec, err := ebpf.LoadCollectionSpec(testutils.NativeFile(t, "../testdata/minimal-%s.elf")) if err != nil { t.Fatal(err) } bar := mustAnyTypeByName(t, spec, "bar") barfoo := mustAnyTypeByName(t, spec, "barfoo") baz := mustAnyTypeByName(t, spec, "baz") e := mustAnyTypeByName(t, spec, "e") ubar := mustAnyTypeByName(t, spec, "ubar") got := CollectGlobalTypes(spec) qt.Assert(t, qt.IsNil(err)) want := []btf.Type{bar, barfoo, baz, e, ubar} qt.Assert(t, qt.CmpEquals(got, want, cmp.Comparer(func(a, b btf.Type) bool { return a.TypeName() == b.TypeName() }))) } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/internal/000077500000000000000000000000001520243672000227355ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/internal/module.go000066400000000000000000000005731520243672000245560ustar00rootroot00000000000000package internal // We used to have some clever code here which relied on debug.ReadBuildInfo(). // This is broken due to https://github.com/golang/go/issues/33976, and some build // systems like bazel also do not generate the necessary data. Let's keep it // simple instead. // The module containing the code in this repository. const CurrentModule = "github.com/cilium/ebpf" golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/main.go000066400000000000000000000330361520243672000224010ustar00rootroot00000000000000//go:build !windows package main import ( "errors" "flag" "fmt" "io" "os" "os/exec" "path/filepath" "regexp" "slices" "sort" "strconv" "strings" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/cmd/bpf2go/gen" ) const helpText = `Usage: %[1]s [options] [-- ] ident is used as the stem of all generated Go types and functions, and must be a valid Go identifier. source is a single C file that is compiled using the specified compiler (usually some version of clang). You can pass options to the compiler by appending them after a '--' argument or by supplying -cflags. Flags passed as arguments take precedence over flags passed via -cflags. Additionally, the program expands quotation marks in -cflags. This means that -cflags 'foo "bar baz"' is passed to the compiler as two arguments "foo" and "bar baz". The program expects GOPACKAGE to be set in the environment, and should be invoked via go generate. The generated files are written to the current directory. Some options take defaults from the environment. Variable name is mentioned next to the respective option. Options: ` func run(stdout io.Writer, args []string) (err error) { b2g, err := newB2G(stdout, args) switch { case err == nil: return b2g.convertAll() case errors.Is(err, flag.ErrHelp): return nil default: return err } } type bpf2go struct { stdout io.Writer verbose bool // Absolute path to a .c file. sourceFile string // Absolute path to a directory where .go are written outputDir string // Alternative output stem. If empty, identStem is used. outputStem string // Suffix in generated file names such as _test. outputSuffix string // Valid go package name. pkg string // Valid go identifier. identStem string // Targets to build for. targetArches map[gen.Target]gen.GoArches // C compiler. cc string // Command used to strip DWARF. strip string disableStripping bool // C flags passed to the compiler. cFlags []string skipGlobalTypes bool // C types to include in the generated output. cTypes cTypes // Build tags to be included in the output. tags buildTags // Base directory of the Makefile. Enables outputting make-style dependencies // in .d files. makeBase string } func (b2g *bpf2go) Debugln(a ...any) { if b2g.verbose { fmt.Fprintln(b2g.stdout, a...) } } func newB2G(stdout io.Writer, args []string) (*bpf2go, error) { b2g := &bpf2go{ stdout: stdout, } fs := flag.NewFlagSet("bpf2go", flag.ContinueOnError) fs.BoolVar(&b2g.verbose, "verbose", getBool("V", false), "Enable verbose logging ($V)") fs.StringVar(&b2g.cc, "cc", getEnv("BPF2GO_CC", "clang"), "`binary` used to compile C to BPF ($BPF2GO_CC)") fs.StringVar(&b2g.strip, "strip", getEnv("BPF2GO_STRIP", ""), "`binary` used to strip DWARF from compiled BPF ($BPF2GO_STRIP)") fs.BoolVar(&b2g.disableStripping, "no-strip", false, "disable stripping of DWARF") flagCFlags := fs.String("cflags", getEnv("BPF2GO_CFLAGS", ""), "flags passed to the compiler, may contain quoted arguments ($BPF2GO_CFLAGS)") fs.Var(&b2g.tags, "tags", "Comma-separated list of Go build tags to include in generated files") flagTarget := fs.String("target", "bpfel,bpfeb", "clang target(s) to compile for (comma separated)") fs.StringVar(&b2g.makeBase, "makebase", getEnv("BPF2GO_MAKEBASE", ""), "write make compatible depinfo files relative to `directory` ($BPF2GO_MAKEBASE)") fs.Var(&b2g.cTypes, "type", "`Name` of a type to generate a Go declaration for, may be repeated") fs.BoolVar(&b2g.skipGlobalTypes, "no-global-types", false, "Skip generating types for map keys and values, etc.") fs.StringVar(&b2g.outputStem, "output-stem", "", "alternative stem for names of generated files (defaults to ident)") outputSuffix := "" if strings.HasSuffix(getEnv("GOFILE", ""), "_test.go") { outputSuffix = "_test" } fs.StringVar(&b2g.outputSuffix, "output-suffix", outputSuffix, "suffix in generated file names such as _test (default based on $GOFILE)") outDir := fs.String("output-dir", "", "target directory of generated files (defaults to current directory)") outPkg := fs.String("go-package", "", "package for output go file (default as ENV GOPACKAGE)") fs.SetOutput(b2g.stdout) fs.Usage = func() { fmt.Fprintf(fs.Output(), helpText, fs.Name()) fs.PrintDefaults() fmt.Fprintln(fs.Output()) printTargets(fs.Output()) } if err := fs.Parse(args); err != nil { return nil, err } if *outDir == "" { var err error if *outDir, err = os.Getwd(); err != nil { return nil, err } } b2g.outputDir = *outDir if *outPkg == "" { *outPkg = os.Getenv(gopackageEnv) } b2g.pkg = *outPkg if b2g.pkg == "" { return nil, errors.New("missing package, you should either set the go-package flag or the GOPACKAGE env") } // Allow CC like "ccache clang" to work. ccParts := strings.Fields(b2g.cc) if len(ccParts) == 0 { return nil, errors.New("no compiler specified") } b2g.cc = ccParts[0] args, cFlags := splitCFlagsFromArgs(fs.Args()) if *flagCFlags != "" { splitCFlags, err := splitArguments(*flagCFlags) if err != nil { return nil, err } // Command line arguments take precedence over C flags // from the flag. cFlags = append(splitCFlags, cFlags...) } for _, cFlag := range cFlags { if strings.HasPrefix(cFlag, "-M") { return nil, fmt.Errorf("use -makebase instead of %q", cFlag) } } b2g.cFlags = append(ccParts[1:], cFlags[:len(cFlags):len(cFlags)]...) if len(args) < 2 { return nil, errors.New("expected at least two arguments") } b2g.identStem = args[0] sourceFile, err := filepath.Abs(args[1]) if err != nil { return nil, err } b2g.sourceFile = sourceFile if b2g.makeBase != "" { b2g.makeBase, err = filepath.Abs(b2g.makeBase) if err != nil { return nil, err } } if b2g.outputStem != "" && strings.ContainsRune(b2g.outputStem, filepath.Separator) { return nil, fmt.Errorf("-output-stem %q must not contain path separation characters", b2g.outputStem) } if strings.ContainsRune(b2g.outputSuffix, filepath.Separator) { return nil, fmt.Errorf("-output-suffix %q must not contain path separation characters", b2g.outputSuffix) } targetArches := make(map[gen.Target]gen.GoArches) for _, tgt := range strings.Split(*flagTarget, ",") { target, goarches, err := gen.FindTarget(tgt) if err != nil { if errors.Is(err, gen.ErrInvalidTarget) { printTargets(b2g.stdout) fmt.Fprintln(b2g.stdout) } return nil, err } targetArches[target] = goarches } if len(targetArches) == 0 { return nil, fmt.Errorf("no targets specified") } b2g.targetArches = targetArches // Try to find a suitable llvm-strip, possibly with a version suffix derived // from the clang binary. if b2g.strip == "" { b2g.strip = "llvm-strip" if after, ok := strings.CutPrefix(b2g.cc, "clang"); ok { b2g.strip += after } } return b2g, nil } // cTypes collects the C type names a user wants to generate Go types for. // // Names are guaranteed to be unique, and only a subset of names is accepted so // that we may extend the flag syntax in the future. type cTypes []string var _ flag.Value = (*cTypes)(nil) func (ct *cTypes) String() string { if ct == nil { return "[]" } return fmt.Sprint(*ct) } const validCTypeChars = `[a-z0-9_]` var reValidCType = regexp.MustCompile(`(?i)^` + validCTypeChars + `+$`) func (ct *cTypes) Set(value string) error { if !reValidCType.MatchString(value) { return fmt.Errorf("%q contains characters outside of %s", value, validCTypeChars) } i := sort.SearchStrings(*ct, value) if i >= len(*ct) { *ct = append(*ct, value) return nil } if (*ct)[i] == value { return fmt.Errorf("duplicate type %q", value) } *ct = append((*ct)[:i], append([]string{value}, (*ct)[i:]...)...) return nil } func getEnv(key, defaultVal string) string { if val, ok := os.LookupEnv(key); ok { return val } return defaultVal } func getBool(key string, defaultVal bool) bool { val, ok := os.LookupEnv(key) if !ok { return defaultVal } b, err := strconv.ParseBool(val) if err != nil { return defaultVal } return b } func (b2g *bpf2go) convertAll() (err error) { if _, err := os.Stat(b2g.sourceFile); os.IsNotExist(err) { return fmt.Errorf("file %s doesn't exist", b2g.sourceFile) } else if err != nil { return err } if !b2g.disableStripping { b2g.strip, err = exec.LookPath(b2g.strip) if err != nil { return err } } for target, arches := range b2g.targetArches { if err := b2g.convert(target, arches); err != nil { return err } } return nil } func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) { removeOnError := func(f *os.File) { if err != nil { os.Remove(f.Name()) } f.Close() } outputStem := b2g.outputStem if outputStem == "" { outputStem = strings.ToLower(b2g.identStem) } stem := fmt.Sprintf("%s_%s%s", outputStem, tgt.Suffix(), b2g.outputSuffix) absOutPath, err := filepath.Abs(b2g.outputDir) if err != nil { return err } objFileName := filepath.Join(absOutPath, stem+".o") cwd, err := os.Getwd() if err != nil { return err } archConstraint := goarches.Constraint() constraints := andConstraints(archConstraint, b2g.tags.Expr) if err := b2g.removeOldOutputFiles(outputStem, tgt); err != nil { return fmt.Errorf("remove obsolete output: %w", err) } var depInput *os.File cFlags := slices.Clone(b2g.cFlags) if b2g.makeBase != "" { depInput, err = os.CreateTemp("", "bpf2go") if err != nil { return err } defer depInput.Close() defer os.Remove(depInput.Name()) cFlags = append(cFlags, // Output dependency information. "-MD", // Create phony targets so that deleting a dependency doesn't // break the build. "-MP", // Write it to temporary file "-MF"+depInput.Name(), ) } err = gen.Compile(gen.CompileArgs{ CC: b2g.cc, Strip: b2g.strip, DisableStripping: b2g.disableStripping, Flags: cFlags, Target: tgt, Workdir: cwd, Source: b2g.sourceFile, Dest: objFileName, }) if err != nil { return fmt.Errorf("compile: %w", err) } if b2g.disableStripping { b2g.Debugln("Compiled object", "file", objFileName) } else { b2g.Debugln("Compiled and stripped object", "file", objFileName) } spec, err := ebpf.LoadCollectionSpec(objFileName) if err != nil { return fmt.Errorf("can't load BPF from ELF: %s", err) } var maps []string for name := range spec.Maps { // Skip .rodata, .data, .bss, etc. sections if !strings.HasPrefix(name, ".") { maps = append(maps, name) } } var variables []string for name := range spec.Variables { variables = append(variables, name) } var programs []string for name := range spec.Programs { programs = append(programs, name) } types, err := collectCTypes(spec.Types, b2g.cTypes) if err != nil { return fmt.Errorf("collect C types: %w", err) } if !b2g.skipGlobalTypes { types = append(types, gen.CollectGlobalTypes(spec)...) } // Write out generated go goFileName := filepath.Join(absOutPath, stem+".go") goFile, err := os.Create(goFileName) if err != nil { return err } defer removeOnError(goFile) err = gen.Generate(gen.GenerateArgs{ Package: b2g.pkg, Stem: b2g.identStem, Constraints: constraints, Maps: maps, Variables: variables, Programs: programs, Types: types, ObjectFile: filepath.Base(objFileName), Output: goFile, }) if err != nil { return fmt.Errorf("can't write %s: %s", goFileName, err) } b2g.Debugln("Generated bpf2go binding", "file", goFileName) if b2g.makeBase == "" { return } deps, err := parseDependencies(cwd, depInput) if err != nil { return fmt.Errorf("can't read dependency information: %s", err) } depFileName := goFileName + ".d" depOutput, err := os.Create(depFileName) if err != nil { return fmt.Errorf("write make dependencies: %w", err) } defer depOutput.Close() // There is always at least a dependency for the main file. deps[0].file = goFileName if err := adjustDependencies(depOutput, b2g.makeBase, deps); err != nil { return fmt.Errorf("can't adjust dependency information: %s", err) } b2g.Debugln("Wrote dependency", "file", depFileName) return nil } // removeOldOutputFiles removes output files generated by an old naming scheme. // // In the old scheme some linux targets were interpreted as build constraints // by the go toolchain. func (b2g *bpf2go) removeOldOutputFiles(outputStem string, tgt gen.Target) error { suffix := tgt.ObsoleteSuffix() if suffix == "" { return nil } stem := fmt.Sprintf("%s_%s", outputStem, suffix) for _, ext := range []string{".o", ".go"} { filename := filepath.Join(b2g.outputDir, stem+ext) if err := os.Remove(filename); errors.Is(err, os.ErrNotExist) { continue } else if err != nil { return err } b2g.Debugln("Removed obsolete output file", "file", filename) } return nil } func printTargets(w io.Writer) { var arches []string for goarch, archTarget := range gen.TargetsByGoArch() { if archTarget.IsGeneric() { continue } arches = append(arches, string(goarch)) } sort.Strings(arches) fmt.Fprint(w, "Supported targets:\n") fmt.Fprint(w, "\tbpf\n\tbpfel\n\tbpfeb\n") for _, arch := range arches { fmt.Fprintf(w, "\t%s\n", arch) } } func collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) { var result []btf.Type for _, cType := range names { typ, err := types.AnyTypeByName(cType) if err != nil { return nil, err } result = append(result, typ) } return result, nil } const gopackageEnv = "GOPACKAGE" func main() { if err := run(os.Stdout, os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/main_test.go000066400000000000000000000255521520243672000234440ustar00rootroot00000000000000//go:build !windows package main import ( "bytes" "fmt" "io" "os" "os/exec" "path/filepath" "strings" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/cmd/bpf2go/gen" "github.com/cilium/ebpf/cmd/bpf2go/internal" "github.com/cilium/ebpf/internal/testutils" ) const minimalSocketFilter = `__attribute__((section("socket"), used)) int main() { return 0; }` func TestRun(t *testing.T) { clangBin := testutils.ClangBin(t) dir := t.TempDir() mustWriteFile(t, dir, "test.c", minimalSocketFilter) modRoot, err := filepath.Abs("../..") qt.Assert(t, qt.IsNil(err)) if _, err := os.Stat(filepath.Join(modRoot, "go.mod")); os.IsNotExist(err) { t.Fatal("No go.mod file in", modRoot) } modDir := t.TempDir() execInModule := func(name string, args ...string) { t.Helper() cmd := exec.Command(name, args...) cmd.Dir = modDir if out, err := cmd.CombinedOutput(); err != nil { if out := string(out); out != "" { t.Log(out) } t.Fatalf("Can't execute %s: %v", name, args) } } module := internal.CurrentModule execInModule("go", "mod", "init", "bpf2go-test") execInModule("go", "mod", "edit", // Require the module. The version doesn't matter due to the replace // below. fmt.Sprintf("-require=%s@v0.0.0", module), // Replace the module with the current version. fmt.Sprintf("-replace=%s=%s", module, modRoot), ) goarches := []string{ "amd64", // little-endian "arm64", "s390x", // big-endian } err = run(io.Discard, []string{ "-go-package", "main", "-output-dir", modDir, "-cc", clangBin, "-target", strings.Join(goarches, ","), "bar", filepath.Join(dir, "test.c"), }) if err != nil { t.Fatal("Can't run:", err) } mustWriteFile(t, modDir, "main.go", ` package main func main() { var obj barObjects println(obj.Main) }`) for _, arch := range goarches { t.Run(arch, func(t *testing.T) { goBuild := exec.Command("go", "build", "-mod=mod", "-o", "/dev/null") goBuild.Dir = modDir goBuild.Env = append(os.Environ(), "GOOS=linux", "GOARCH="+arch, "GOPROXY=off", "GOSUMDB=off", ) out, err := goBuild.CombinedOutput() if err != nil { if out := string(out); out != "" { t.Log(out) } t.Error("Can't compile package:", err) } }) } } func TestHelp(t *testing.T) { var stdout bytes.Buffer err := run(&stdout, []string{"-help"}) if err != nil { t.Fatal("Can't execute -help") } if stdout.Len() == 0 { t.Error("-help doesn't write to stdout") } } func TestErrorMentionsEnvVar(t *testing.T) { err := run(io.Discard, nil) qt.Assert(t, qt.StringContains(err.Error(), gopackageEnv), qt.Commentf("Error should include name of environment variable")) } func TestDisableStripping(t *testing.T) { dir := t.TempDir() mustWriteFile(t, dir, "test.c", minimalSocketFilter) err := run(io.Discard, []string{ "-go-package", "foo", "-output-dir", dir, "-cc", testutils.ClangBin(t), "-strip", "binary-that-certainly-doesnt-exist", "-no-strip", "bar", filepath.Join(dir, "test.c"), }) if err != nil { t.Fatal("Can't run with stripping disabled:", err) } } func TestConvertGOARCH(t *testing.T) { tmp := t.TempDir() mustWriteFile(t, tmp, "test.c", ` #ifndef __TARGET_ARCH_x86 #error __TARGET_ARCH_x86 is not defined #endif`, ) b2g := bpf2go{ pkg: "test", stdout: io.Discard, identStem: "test", cc: testutils.ClangBin(t), disableStripping: true, sourceFile: tmp + "/test.c", outputDir: tmp, } if err := b2g.convert(gen.TargetsByGoArch()["amd64"], nil); err != nil { t.Fatal("Can't target GOARCH:", err) } } func TestCTypes(t *testing.T) { var ct cTypes valid := []string{ "abcdefghijklmnopqrstuvqxyABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_", "y", } for _, value := range valid { if err := ct.Set(value); err != nil { t.Fatalf("Set returned an error for %q: %s", value, err) } } qt.Assert(t, qt.ContentEquals(ct, valid)) for _, value := range []string{ "", " ", " frood", "foo\nbar", ".", ",", "+", "-", } { ct = nil if err := ct.Set(value); err == nil { t.Fatalf("Set did not return an error for %q", value) } } ct = nil qt.Assert(t, qt.IsNil(ct.Set("foo"))) qt.Assert(t, qt.IsNotNil(ct.Set("foo"))) } func TestParseArgs(t *testing.T) { const ( pkg = "eee" outputDir = "." csource = "testdata/minimal.c" stem = "a" ) t.Run("makebase", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) basePath, _ := filepath.Abs("barfoo") args := []string{"-makebase", basePath, stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.makeBase, basePath)) }) t.Run("makebase from env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) basePath, _ := filepath.Abs("barfoo") args := []string{stem, csource} t.Setenv("BPF2GO_MAKEBASE", basePath) b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.makeBase, basePath)) }) t.Run("makebase flag overrides env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) basePathFlag, _ := filepath.Abs("barfoo") basePathEnv, _ := filepath.Abs("foobar") args := []string{"-makebase", basePathFlag, stem, csource} t.Setenv("BPF2GO_MAKEBASE", basePathEnv) b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.makeBase, basePathFlag)) }) t.Run("cc defaults to clang", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.cc, "clang")) }) t.Run("cc", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-cc", "barfoo", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.cc, "barfoo")) }) t.Run("cc from env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{stem, csource} t.Setenv("BPF2GO_CC", "barfoo") b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.cc, "barfoo")) }) t.Run("cc flag overrides env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-cc", "barfoo", stem, csource} t.Setenv("BPF2GO_CC", "foobar") b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.cc, "barfoo")) }) t.Run("strip defaults to llvm-strip", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.strip, "llvm-strip")) }) t.Run("strip", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-strip", "barfoo", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.strip, "barfoo")) }) t.Run("strip from env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{stem, csource} t.Setenv("BPF2GO_STRIP", "barfoo") b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.strip, "barfoo")) }) t.Run("strip flag overrides env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-strip", "barfoo", stem, csource} t.Setenv("BPF2GO_STRIP", "foobar") b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.strip, "barfoo")) }) t.Run("no strip defaults to false", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsFalse(b2g.disableStripping)) }) t.Run("no strip", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-no-strip", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(b2g.disableStripping)) }) t.Run("cflags flag", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-cflags", "x y z", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z"})) }) t.Run("cflags multi flag", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-cflags", "x y z", "-cflags", "u v", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"u", "v"})) }) t.Run("cflags flag and args", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-cflags", "x y z", "stem", csource, "--", "u", "v"} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z", "u", "v"})) }) t.Run("cflags from env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{stem, csource} t.Setenv("BPF2GO_CFLAGS", "x y z") b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"x", "y", "z"})) }) t.Run("cflags flag overrides env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-cflags", "u v", stem, csource} t.Setenv("BPF2GO_CFLAGS", "x y z") b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{"u", "v"})) }) t.Run("go package overrides env", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-go-package", "aaa", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.pkg, "aaa")) }) t.Run("output dir", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) args := []string{"-output-dir", outputDir, stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.outputDir, outputDir)) }) t.Run("output suffix default", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) b2g, err := newB2G(&bytes.Buffer{}, []string{stem, csource}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.outputSuffix, "")) }) t.Run("output suffix GOFILE=_test", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) t.Setenv("GOFILE", "foo_test.go") b2g, err := newB2G(&bytes.Buffer{}, []string{stem, csource}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.outputSuffix, "_test")) }) t.Run("output suffix custom", func(t *testing.T) { t.Setenv(gopackageEnv, pkg) t.Setenv("GOFILE", "foo_test.go") args := []string{"-output-suffix", "_custom", stem, csource} b2g, err := newB2G(&bytes.Buffer{}, args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(b2g.outputSuffix, "_custom")) }) } func mustWriteFile(tb testing.TB, dir, name, contents string) { tb.Helper() tmpFile := filepath.Join(dir, name) if err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil { tb.Fatal(err) } } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/makedep.go000066400000000000000000000041661520243672000230650ustar00rootroot00000000000000//go:build !windows package main import ( "bufio" "bytes" "errors" "fmt" "io" "path/filepath" "strings" ) func adjustDependencies(w io.Writer, baseDir string, deps []dependency) error { for _, dep := range deps { relativeFile, err := filepath.Rel(baseDir, dep.file) if err != nil { return err } if len(dep.prerequisites) == 0 { _, err := fmt.Fprintf(w, "%s:\n\n", relativeFile) if err != nil { return err } continue } var prereqs []string for _, prereq := range dep.prerequisites { relativePrereq, err := filepath.Rel(baseDir, prereq) if err != nil { return err } prereqs = append(prereqs, relativePrereq) } _, err = fmt.Fprintf(w, "%s: \\\n %s\n\n", relativeFile, strings.Join(prereqs, " \\\n ")) if err != nil { return err } } return nil } type dependency struct { file string prerequisites []string } func parseDependencies(baseDir string, in io.Reader) ([]dependency, error) { abs := func(path string) string { if filepath.IsAbs(path) { return path } return filepath.Join(baseDir, path) } scanner := bufio.NewScanner(in) var line strings.Builder var deps []dependency for scanner.Scan() { buf := scanner.Bytes() if line.Len()+len(buf) > 1024*1024 { return nil, errors.New("line too long") } if bytes.HasSuffix(buf, []byte{'\\'}) { line.Write(buf[:len(buf)-1]) continue } line.Write(buf) if line.Len() == 0 { // Skip empty lines continue } parts := strings.SplitN(line.String(), ":", 2) if len(parts) < 2 { return nil, fmt.Errorf("invalid line without ':'") } // NB: This doesn't handle filenames with spaces in them. // It seems like make doesn't do that either, so oh well. var prereqs []string for _, prereq := range strings.Fields(parts[1]) { prereqs = append(prereqs, abs(prereq)) } deps = append(deps, dependency{ abs(string(parts[0])), prereqs, }) line.Reset() } if err := scanner.Err(); err != nil { return nil, err } // There is always at least a dependency for the main file. if len(deps) == 0 { return nil, fmt.Errorf("empty dependency file") } return deps, nil } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/makedep_test.go000066400000000000000000000017521520243672000241220ustar00rootroot00000000000000//go:build !windows package main import ( "bytes" "reflect" "strings" "testing" ) func TestParseDependencies(t *testing.T) { const input = `main.go: /foo/bar baz frob: /gobble \ gubble nothing: ` have, err := parseDependencies("/foo", strings.NewReader(input)) if err != nil { t.Fatal("Can't parse dependencies:", err) } want := []dependency{ {"/foo/main.go", []string{"/foo/bar", "/foo/baz"}}, {"/foo/frob", []string{"/gobble", "/foo/gubble"}}, {"/foo/nothing", nil}, } if !reflect.DeepEqual(have, want) { t.Logf("Have: %#v", have) t.Logf("Want: %#v", want) t.Error("Result doesn't match") } var output bytes.Buffer err = adjustDependencies(&output, "/foo", want) if err != nil { t.Error("Can't adjust dependencies") } const wantOutput = `main.go: \ bar \ baz frob: \ ../gobble \ gubble nothing: ` if have := output.String(); have != wantOutput { t.Logf("Have:\n%s", have) t.Logf("Want:\n%s", wantOutput) t.Error("Output doesn't match") } } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/testdata/000077500000000000000000000000001520243672000227325ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/testdata/minimal-eb.elf000066400000000000000000000065501520243672000254420ustar00rootroot00000000000000ELF @@ayMITx  !' . 2 @6`: B@L RW@[am  r        !2"#$@% (.#+)@-*6 C' -N)+#S,]+.b h&pint__ARRAY_SIZE_TYPE__eHOOPYFROODbarfoobarbazboolongintlong long_Booltypekeyvaluemax_entriesmap1filtersocket/ebpf/cmd/bpf2go/testdata/minimal.c return my_constant + struct_const.bar;char__licensemy_constantstruct_constan_intint_arrayauint64_tunsigned longstruct_arraybuint32_tunsigned intstruct_varubarunion_var.bss.maps.rodatalicense L`yy $0 8a@?+ 8 (hs` l  ,@P`p int_arraystruct_array.text.rel.BTF.extstruct_constan_intmy_constant.relsocket.bss.mapsfilterstruct_varunion_var.llvm_addrsig__license.strtab.symtab.rodata.rel.BTFmap1@O@@K @ 8[ Vd @ "D @P }oL golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/testdata/minimal-el.elf000066400000000000000000000065501520243672000254540ustar00rootroot00000000000000ELF @@ayMITx  !' . 2 @6`: B@L RW@[am  r        !2"#$@% (.#+)@-*6 C' -N)+#S,]+.b h&pint__ARRAY_SIZE_TYPE__eHOOPYFROODbarfoobarbazboolongintlong long_Booltypekeyvaluemax_entriesmap1filtersocket/ebpf/cmd/bpf2go/testdata/minimal.c return my_constant + struct_const.bar;char__licensemy_constantstruct_constan_intint_arrayauint64_tunsigned longstruct_arraybuint32_tunsigned intstruct_varubarunion_var.bss.maps.rodatalicense L`yy $0 8a@?+ 8 (hs` l  ,@P`p int_arraystruct_array.text.rel.BTF.extstruct_constan_intmy_constant.relsocket.bss.mapsfilterstruct_varunion_var.llvm_addrsig__license.strtab.symtab.rodata.rel.BTFmap1@O@@K @ 8[ Vd @ "D @P }Lo golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/testdata/minimal.c000066400000000000000000000014331520243672000245250ustar00rootroot00000000000000#include "../../../testdata/common.h" char __license[] __section("license") = "MIT"; enum e { HOOPY, FROOD }; typedef long long int longint; typedef struct { longint bar; _Bool baz; enum e boo; } barfoo; typedef struct { uint64_t a; } baz; struct bar { uint64_t a; uint32_t b; }; union ubar { uint32_t a; uint64_t b; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, enum e); __type(value, barfoo); __uint(max_entries, 1); } map1 __section(".maps"); volatile const int an_int; volatile const enum e my_constant = FROOD; volatile const int int_array[2]; volatile const barfoo struct_const; volatile const baz struct_array[2]; volatile struct bar struct_var; volatile union ubar union_var; __section("socket") int filter() { return my_constant + struct_const.bar; } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/tools.go000066400000000000000000000022111520243672000226040ustar00rootroot00000000000000//go:build !windows package main import ( "errors" "fmt" "strings" ) func splitCFlagsFromArgs(in []string) (args, cflags []string) { for i, arg := range in { if arg == "--" { return in[:i], in[i+1:] } } return in, nil } func splitArguments(in string) ([]string, error) { var ( result []string builder strings.Builder escaped bool delim = ' ' ) for _, r := range strings.TrimSpace(in) { if escaped { builder.WriteRune(r) escaped = false continue } switch r { case '\\': escaped = true case delim: current := builder.String() builder.Reset() if current != "" || delim != ' ' { // Only append empty words if they are not // delimited by spaces result = append(result, current) } delim = ' ' case '"', '\'', ' ': if delim == ' ' { delim = r continue } fallthrough default: builder.WriteRune(r) } } if delim != ' ' { return nil, fmt.Errorf("missing `%c`", delim) } if escaped { return nil, errors.New("unfinished escape") } // Add the last word if builder.Len() > 0 { result = append(result, builder.String()) } return result, nil } golang-github-cilium-ebpf-0.21.0+ds1/cmd/bpf2go/tools_test.go000066400000000000000000000016751520243672000236600ustar00rootroot00000000000000//go:build !windows package main import ( "reflect" "testing" ) func TestSplitArguments(t *testing.T) { testcases := []struct { in string out []string }{ {`foo`, []string{"foo"}}, {`foo bar`, []string{"foo", "bar"}}, {`foo bar`, []string{"foo", "bar"}}, {`\\`, []string{`\`}}, {`\\\`, nil}, {`foo\ bar`, []string{"foo bar"}}, {`foo "" bar`, []string{"foo", "", "bar"}}, {`"bar baz"`, []string{"bar baz"}}, {`'bar baz'`, []string{"bar baz"}}, {`'bar " " baz'`, []string{`bar " " baz`}}, {`"bar \" baz"`, []string{`bar " baz`}}, {`"`, nil}, {`'`, nil}, } for _, testcase := range testcases { have, err := splitArguments(testcase.in) if testcase.out == nil { if err == nil { t.Errorf("Test should fail for: %s", testcase.in) } } else if !reflect.DeepEqual(testcase.out, have) { t.Logf("Have: %q\n", have) t.Logf("Want: %q\n", testcase.out) t.Errorf("Test fails for: %s", testcase.in) } } } golang-github-cilium-ebpf-0.21.0+ds1/collection.go000066400000000000000000000727151520243672000216750ustar00rootroot00000000000000package ebpf import ( "encoding/binary" "errors" "fmt" "path/filepath" "reflect" "runtime" "slices" "strings" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/kallsyms" "github.com/cilium/ebpf/internal/kconfig" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" ) // CollectionOptions control loading a collection into the kernel. // // Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions. type CollectionOptions struct { Maps MapOptions Programs ProgramOptions // MapReplacements takes a set of Maps that will be used instead of // creating new ones when loading the CollectionSpec. // // For each given Map, there must be a corresponding MapSpec in // CollectionSpec.Maps, and its type, key/value size, max entries and flags // must match the values of the MapSpec. // // The given Maps are Clone()d before being used in the Collection, so the // caller can Close() them freely when they are no longer needed. MapReplacements map[string]*Map } // CollectionSpec describes a collection. type CollectionSpec struct { Maps map[string]*MapSpec Programs map[string]*ProgramSpec // Variables refer to global variables declared in the ELF. They can be read // and modified freely before loading the Collection. Modifying them after // loading has no effect on a running eBPF program. Variables map[string]*VariableSpec // Types holds type information about Maps and Programs. // Modifications to Types are currently undefined behaviour. Types *btf.Spec // ByteOrder specifies whether the ELF was compiled for // big-endian or little-endian architectures. ByteOrder binary.ByteOrder } // Copy returns a recursive copy of the spec. func (cs *CollectionSpec) Copy() *CollectionSpec { if cs == nil { return nil } cpy := CollectionSpec{ Maps: copyMapOfSpecs(cs.Maps), Programs: copyMapOfSpecs(cs.Programs), Variables: make(map[string]*VariableSpec, len(cs.Variables)), ByteOrder: cs.ByteOrder, Types: cs.Types.Copy(), } for name, spec := range cs.Variables { cpy.Variables[name] = spec.Copy() } if cs.Variables == nil { cpy.Variables = nil } return &cpy } func copyMapOfSpecs[T interface{ Copy() T }](m map[string]T) map[string]T { if m == nil { return nil } cpy := make(map[string]T, len(m)) for k, v := range m { cpy[k] = v.Copy() } return cpy } // Assign the contents of a CollectionSpec to a struct. // // This function is a shortcut to manually checking the presence // of maps and programs in a CollectionSpec. Consider using bpf2go // if this sounds useful. // // 'to' must be a pointer to a struct. A field of the // struct is updated with values from Programs, Maps or Variables if it // has an `ebpf` tag and its type is *ProgramSpec, *MapSpec or *VariableSpec. // The tag's value specifies the name of the program or map as // found in the CollectionSpec. // // struct { // Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` // Bar *ebpf.MapSpec `ebpf:"bar_map"` // Var *ebpf.VariableSpec `ebpf:"some_var"` // Ignored int // } // // Returns an error if any of the eBPF objects can't be found, or // if the same Spec is assigned multiple times. func (cs *CollectionSpec) Assign(to interface{}) error { getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { case reflect.TypeOf((*ProgramSpec)(nil)): if p := cs.Programs[name]; p != nil { return p, nil } return nil, fmt.Errorf("missing program %q", name) case reflect.TypeOf((*MapSpec)(nil)): if m := cs.Maps[name]; m != nil { return m, nil } return nil, fmt.Errorf("missing map %q", name) case reflect.TypeOf((*VariableSpec)(nil)): if v := cs.Variables[name]; v != nil { return v, nil } return nil, fmt.Errorf("missing variable %q", name) default: return nil, fmt.Errorf("unsupported type %s", typ) } } return assignValues(to, getValue) } // LoadAndAssign loads Maps and Programs into the kernel and assigns them // to a struct. // // Omitting Map/Program.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. // // This function is a shortcut to manually checking the presence // of maps and programs in a CollectionSpec. Consider using bpf2go // if this sounds useful. // // 'to' must be a pointer to a struct. A field of the struct is updated with // a Program or Map if it has an `ebpf` tag and its type is *Program or *Map. // The tag's value specifies the name of the program or map as found in the // CollectionSpec. Before updating the struct, the requested objects and their // dependent resources are loaded into the kernel and populated with values if // specified. // // struct { // Foo *ebpf.Program `ebpf:"xdp_foo"` // Bar *ebpf.Map `ebpf:"bar_map"` // Ignored int // } // // opts may be nil. // // Returns an error if any of the fields can't be found, or // if the same Map or Program is assigned multiple times. func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { loader, err := newCollectionLoader(cs, opts) if err != nil { return err } defer loader.close() // Support assigning Programs and Maps, lazy-loading the required objects. assignedMaps := make(map[string]bool) assignedProgs := make(map[string]bool) assignedVars := make(map[string]bool) getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { case reflect.TypeOf((*Program)(nil)): assignedProgs[name] = true return loader.loadProgram(name) case reflect.TypeOf((*Map)(nil)): assignedMaps[name] = true return loader.loadMap(name) case reflect.TypeOf((*Variable)(nil)): assignedVars[name] = true return loader.loadVariable(name) default: return nil, fmt.Errorf("unsupported type %s", typ) } } // Load the Maps and Programs requested by the annotated struct. if err := assignValues(to, getValue); err != nil { return err } // Populate the requested maps. Has a chance of lazy-loading other dependent maps. if err := loader.populateDeferredMaps(); err != nil { return err } // Evaluate the loader's objects after all (lazy)loading has taken place. for n, m := range loader.maps { if m.typ.canStoreProgram() { // Require all lazy-loaded ProgramArrays to be assigned to the given object. // The kernel empties a ProgramArray once the last user space reference // to it closes, which leads to failed tail calls. Combined with the library // closing map fds via GC finalizers this can lead to surprising behaviour. // Only allow unassigned ProgramArrays when the library hasn't pre-populated // any entries from static value declarations. At this point, we know the map // is empty and there's no way for the caller to interact with the map going // forward. if !assignedMaps[n] && len(cs.Maps[n].Contents) > 0 { return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n) } } } // Prevent loader.cleanup() from closing assigned Maps and Programs. for m := range assignedMaps { delete(loader.maps, m) } for p := range assignedProgs { delete(loader.programs, p) } for p := range assignedVars { delete(loader.vars, p) } return nil } // Collection is a collection of live BPF resources present in the kernel. type Collection struct { Programs map[string]*Program Maps map[string]*Map // Variables contains global variables used by the Collection's program(s). On // kernels older than 5.5, most interactions with Variables return // [ErrNotSupported]. Variables map[string]*Variable } // NewCollection creates a Collection from the given spec, creating and // loading its declared resources into the kernel. // // Omitting Collection.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. func NewCollection(spec *CollectionSpec) (*Collection, error) { return NewCollectionWithOptions(spec, CollectionOptions{}) } // NewCollectionWithOptions creates a Collection from the given spec using // options, creating and loading its declared resources into the kernel. // // Omitting Collection.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { loader, err := newCollectionLoader(spec, &opts) if err != nil { return nil, err } defer loader.close() // Create maps first, as their fds need to be linked into programs. for mapName := range spec.Maps { if _, err := loader.loadMap(mapName); err != nil { return nil, err } } for progName, prog := range spec.Programs { if prog.Type == UnspecifiedProgram { continue } if _, err := loader.loadProgram(progName); err != nil { return nil, err } } for varName := range spec.Variables { if _, err := loader.loadVariable(varName); err != nil { return nil, err } } // Maps can contain Program and Map stubs, so populate them after // all Maps and Programs have been successfully loaded. if err := loader.populateDeferredMaps(); err != nil { return nil, err } // Prevent loader.cleanup from closing maps, programs and vars. maps, progs, vars := loader.maps, loader.programs, loader.vars loader.maps, loader.programs, loader.vars = nil, nil, nil return &Collection{ progs, maps, vars, }, nil } type collectionLoader struct { coll *CollectionSpec opts *CollectionOptions maps map[string]*Map programs map[string]*Program vars map[string]*Variable types *btf.Cache } func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) { if opts == nil { opts = &CollectionOptions{} } // Check for existing MapSpecs in the CollectionSpec for all provided replacement maps. for name := range opts.MapReplacements { if _, ok := coll.Maps[name]; !ok { return nil, fmt.Errorf("replacement map %s not found in CollectionSpec", name) } } if err := populateKallsyms(coll.Programs); err != nil { return nil, fmt.Errorf("populating kallsyms caches: %w", err) } return &collectionLoader{ coll, opts, make(map[string]*Map), make(map[string]*Program), make(map[string]*Variable), btf.NewCache(), }, nil } // populateKallsyms populates kallsyms caches, making lookups cheaper later on // during individual program loading. Since we have less context available // at those stages, we batch the lookups here instead to avoid redundant work. func populateKallsyms(progs map[string]*ProgramSpec) error { // Look up addresses of all kernel symbols referenced by all programs. addrs := make(map[string]uint64) for _, p := range progs { iter := p.Instructions.Iterate() for iter.Next() { ins := iter.Ins meta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta) if meta != nil { addrs[meta.Name] = 0 } } } if len(addrs) != 0 { if err := kallsyms.AssignAddresses(addrs); err != nil { return fmt.Errorf("getting addresses from kallsyms: %w", err) } } return nil } // close all resources left over in the collectionLoader. func (cl *collectionLoader) close() { for _, m := range cl.maps { m.Close() } for _, p := range cl.programs { p.Close() } } func (cl *collectionLoader) loadMap(mapName string) (*Map, error) { if m := cl.maps[mapName]; m != nil { return m, nil } mapSpec := cl.coll.Maps[mapName] if mapSpec == nil { return nil, fmt.Errorf("missing map %s", mapName) } mapSpec = mapSpec.Copy() // Defer setting the mmapable flag on maps until load time. This avoids the // MapSpec having different flags on some kernel versions. Also avoid running // syscalls during ELF loading, so platforms like wasm can also parse an ELF. if isDataSection(mapSpec.Name) && haveMmapableMaps() == nil { mapSpec.Flags |= sys.BPF_F_MMAPABLE } if replaceMap, ok := cl.opts.MapReplacements[mapName]; ok { // Check compatibility with the replacement map after setting // feature-dependent map flags. if err := mapSpec.Compatible(replaceMap); err != nil { return nil, fmt.Errorf("using replacement map %s: %w", mapSpec.Name, err) } // Clone the map to avoid closing user's map later on. m, err := replaceMap.Clone() if err != nil { return nil, err } cl.maps[mapName] = m return m, nil } if err := mapSpec.updateDataSection(cl.coll.Variables, mapName); err != nil { return nil, fmt.Errorf("assembling contents of map %s: %w", mapName, err) } m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.types) if err != nil { return nil, fmt.Errorf("map %s: %w", mapName, err) } // Finalize 'scalar' maps that don't refer to any other eBPF resources // potentially pending creation. This is needed for frozen maps like .rodata // that need to be finalized before invoking the verifier. if !mapSpec.Type.canStoreMapOrProgram() { if err := m.finalize(mapSpec); err != nil { _ = m.Close() return nil, fmt.Errorf("finalizing map %s: %w", mapName, err) } } cl.maps[mapName] = m return m, nil } func (cl *collectionLoader) loadProgram(progName string) (*Program, error) { if prog := cl.programs[progName]; prog != nil { return prog, nil } progSpec := cl.coll.Programs[progName] if progSpec == nil { return nil, fmt.Errorf("unknown program %s", progName) } // Bail out early if we know the kernel is going to reject the program. // This skips loading map dependencies, saving some cleanup work later. if progSpec.Type == UnspecifiedProgram { return nil, fmt.Errorf("cannot load program %s: program type is unspecified", progName) } progSpec = progSpec.Copy() // Rewrite any reference to a valid map in the program's instructions, // which includes all of its dependencies. for i := range progSpec.Instructions { ins := &progSpec.Instructions[i] if !ins.IsLoadFromMap() || ins.Reference() == "" { continue } // Don't overwrite map loads containing non-zero map fd's, // they can be manually included by the caller. // Map FDs/IDs are placed in the lower 32 bits of Constant. if int32(ins.Constant) > 0 { continue } m, err := cl.loadMap(ins.Reference()) if err != nil { return nil, fmt.Errorf("program %s: %w", progName, err) } if err := ins.AssociateMap(m); err != nil { return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference(), err) } } prog, err := newProgramWithOptions(progSpec, cl.opts.Programs, cl.types) if err != nil { return nil, fmt.Errorf("program %s: %w", progName, err) } cl.programs[progName] = prog return prog, nil } func (cl *collectionLoader) loadVariable(varName string) (*Variable, error) { if v := cl.vars[varName]; v != nil { return v, nil } varSpec := cl.coll.Variables[varName] if varSpec == nil { return nil, fmt.Errorf("unknown variable %s", varName) } m, err := cl.loadMap(varSpec.SectionName) if err != nil { return nil, fmt.Errorf("variable %s: %w", varName, err) } // If the kernel is too old or the underlying map was created without // BPF_F_MMAPABLE, [Map.Memory] will return ErrNotSupported. In this case, // emit a Variable with a nil Memory. This keeps Collection{Spec}.Variables // consistent across systems with different feature sets without breaking // LoadAndAssign. var mm *Memory if unsafeMemory { mm, err = m.unsafeMemory() } else { mm, err = m.Memory() } if err != nil && !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("variable %s: getting memory for map %s: %w", varName, varSpec.SectionName, err) } v, err := newVariable( varSpec.Name, varSpec.Offset, varSpec.Size(), varSpec.Type, mm, ) if err != nil { return nil, fmt.Errorf("variable %s: %w", varName, err) } cl.vars[varName] = v return v, nil } // populateDeferredMaps iterates maps holding programs or other maps and loads // any dependencies. Populates all maps in cl and freezes them if specified. func (cl *collectionLoader) populateDeferredMaps() error { for mapName, m := range cl.maps { mapSpec, ok := cl.coll.Maps[mapName] if !ok { return fmt.Errorf("missing map spec %s", mapName) } // Scalar maps without Map or Program references are finalized during // creation. Don't finalize them again. if !mapSpec.Type.canStoreMapOrProgram() { continue } mapSpec = mapSpec.Copy() // MapSpecs that refer to inner maps or programs within the same // CollectionSpec do so using strings. These strings are used as the key // to look up the respective object in the Maps or Programs fields. // Resolve those references to actual Map or Program resources that // have been loaded into the kernel. for i, kv := range mapSpec.Contents { objName, ok := kv.Value.(string) if !ok { continue } switch t := mapSpec.Type; { case t.canStoreProgram(): // loadProgram is idempotent and could return an existing Program. prog, err := cl.loadProgram(objName) if err != nil { return fmt.Errorf("loading program %s, for map %s: %w", objName, mapName, err) } mapSpec.Contents[i] = MapKV{kv.Key, prog} case t.canStoreMap(): // loadMap is idempotent and could return an existing Map. innerMap, err := cl.loadMap(objName) if err != nil { return fmt.Errorf("loading inner map %s, for map %s: %w", objName, mapName, err) } mapSpec.Contents[i] = MapKV{kv.Key, innerMap} } } if mapSpec.Type == StructOpsMap { // populate StructOps data into `kernVData` if err := cl.populateStructOps(m, mapSpec); err != nil { return err } } // Populate and freeze the map if specified. if err := m.finalize(mapSpec); err != nil { return fmt.Errorf("populating map %s: %w", mapName, err) } } return nil } // populateStructOps translates the user struct bytes into the kernel value struct // layout for a struct_ops map and writes the result back to mapSpec.Contents[0]. func (cl *collectionLoader) populateStructOps(m *Map, mapSpec *MapSpec) error { userType, ok := btf.As[*btf.Struct](mapSpec.Value) if !ok { return fmt.Errorf("value should be a *Struct") } userData, err := mapSpec.dataSection() if err != nil { return fmt.Errorf("getting data section: %w", err) } if len(userData) < int(userType.Size) { return fmt.Errorf("user data too short: have %d, need at least %d", len(userData), userType.Size) } vType, _, module, err := structOpsFindTarget(userType, cl.types) if err != nil { return fmt.Errorf("struct_ops value type %q: %w", userType.Name, err) } defer module.Close() // Find the inner ops struct embedded in the value struct. kType, kTypeOff, err := structOpsFindInnerType(vType) if err != nil { return err } kernVData := make([]byte, int(vType.Size)) for _, m := range userType.Members { i := slices.IndexFunc(kType.Members, func(km btf.Member) bool { return km.Name == m.Name }) // Allow field to not exist in target as long as the source is zero. if i == -1 { mSize, err := btf.Sizeof(m.Type) if err != nil { return fmt.Errorf("sizeof(user.%s): %w", m.Name, err) } srcOff := int(m.Offset.Bytes()) if srcOff < 0 || srcOff+mSize > len(userData) { return fmt.Errorf("member %q: userdata is too small", m.Name) } // let fail if the field in type user type is missing in type kern type if !structOpsIsMemZeroed(userData[srcOff : srcOff+mSize]) { return fmt.Errorf("%s doesn't exist in %s, but it has non-zero value", m.Name, kType.Name) } continue } km := kType.Members[i] switch btf.UnderlyingType(m.Type).(type) { case *btf.Pointer: // If this is a pointer → resolve struct_ops program. psKey := kType.Name + ":" + m.Name for k, ps := range cl.coll.Programs { if ps.AttachTo == psKey { p, ok := cl.programs[k] if !ok || p == nil { return nil } if err := structOpsPopulateValue(km, kernVData[kTypeOff:], p); err != nil { return err } } } default: // Otherwise → memcpy the field contents. if err := structOpsCopyMember(m, km, userData, kernVData[kTypeOff:]); err != nil { return fmt.Errorf("field %s: %w", kType.Name, err) } } } // Populate the map explicitly and keep a reference on cl.programs. // This is necessary because we may inline fds into kernVData which // may become invalid if the GC frees them. if err := m.Put(uint32(0), kernVData); err != nil { return err } mapSpec.Contents = nil runtime.KeepAlive(cl.programs) return nil } // resolveKconfig resolves all variables declared in .kconfig and populates // m.Contents. Does nothing if the given m.Contents is non-empty. func resolveKconfig(m *MapSpec) error { ds, ok := m.Value.(*btf.Datasec) if !ok { return errors.New("map value is not a Datasec") } if platform.IsWindows { return fmt.Errorf(".kconfig: %w", internal.ErrNotSupportedOnOS) } type configInfo struct { offset uint32 size uint32 typ btf.Type } configs := make(map[string]configInfo) data := make([]byte, ds.Size) for _, vsi := range ds.Vars { v := vsi.Type.(*btf.Var) n := v.TypeName() switch n { case "LINUX_KERNEL_VERSION": if integer, ok := v.Type.(*btf.Int); !ok || integer.Size != 4 { return fmt.Errorf("variable %s must be a 32 bits integer, got %s", n, v.Type) } kv, err := linux.KernelVersion() if err != nil { return fmt.Errorf("getting kernel version: %w", err) } internal.NativeEndian.PutUint32(data[vsi.Offset:], kv.Kernel()) case "LINUX_HAS_SYSCALL_WRAPPER": integer, ok := v.Type.(*btf.Int) if !ok { return fmt.Errorf("variable %s must be an integer, got %s", n, v.Type) } var value uint64 = 1 if err := haveSyscallWrapper(); errors.Is(err, ErrNotSupported) { value = 0 } else if err != nil { return fmt.Errorf("unable to derive a value for LINUX_HAS_SYSCALL_WRAPPER: %w", err) } if err := kconfig.PutInteger(data[vsi.Offset:], integer, value); err != nil { return fmt.Errorf("set LINUX_HAS_SYSCALL_WRAPPER: %w", err) } default: // Catch CONFIG_*. configs[n] = configInfo{ offset: vsi.Offset, size: vsi.Size, typ: v.Type, } } } // We only parse kconfig file if a CONFIG_* variable was found. if len(configs) > 0 { f, err := linux.FindKConfig() if err != nil { return fmt.Errorf("cannot find a kconfig file: %w", err) } defer f.Close() filter := make(map[string]struct{}, len(configs)) for config := range configs { filter[config] = struct{}{} } kernelConfig, err := kconfig.Parse(f, filter) if err != nil { return fmt.Errorf("cannot parse kconfig file: %w", err) } for n, info := range configs { value, ok := kernelConfig[n] if !ok { return fmt.Errorf("config option %q does not exist on this kernel", n) } err := kconfig.PutValue(data[info.offset:info.offset+info.size], info.typ, value) if err != nil { return fmt.Errorf("problem adding value for %s: %w", n, err) } } } m.Contents = []MapKV{{uint32(0), data}} return nil } // LoadCollection reads an object file and creates and loads its declared // resources into the kernel. // // Omitting Collection.Close() during application shutdown is an error. // See the package documentation for details around Map and Program lifecycle. func LoadCollection(file string) (*Collection, error) { if platform.IsWindows { // This mirrors a check in efW. if ext := filepath.Ext(file); ext == ".sys" { return loadCollectionFromNativeImage(file) } } spec, err := LoadCollectionSpec(file) if err != nil { return nil, err } return NewCollection(spec) } // Assign the contents of a Collection to a struct. // // This function bridges functionality between bpf2go generated // code and any functionality better implemented in Collection. // // 'to' must be a pointer to a struct. A field of the // struct is updated with values from Programs or Maps if it // has an `ebpf` tag and its type is *Program or *Map. // The tag's value specifies the name of the program or map as // found in the CollectionSpec. // // struct { // Foo *ebpf.Program `ebpf:"xdp_foo"` // Bar *ebpf.Map `ebpf:"bar_map"` // Ignored int // } // // Returns an error if any of the eBPF objects can't be found, or // if the same Map or Program is assigned multiple times. // // Ownership and Close()ing responsibility is transferred to `to` // for any successful assigns. On error `to` is left in an undefined state. func (coll *Collection) Assign(to interface{}) error { assignedMaps := make(map[string]bool) assignedProgs := make(map[string]bool) assignedVars := make(map[string]bool) // Assign() only transfers already-loaded Maps and Programs. No extra // loading is done. getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { case reflect.TypeOf((*Program)(nil)): if p := coll.Programs[name]; p != nil { assignedProgs[name] = true return p, nil } return nil, fmt.Errorf("missing program %q", name) case reflect.TypeOf((*Map)(nil)): if m := coll.Maps[name]; m != nil { assignedMaps[name] = true return m, nil } return nil, fmt.Errorf("missing map %q", name) case reflect.TypeOf((*Variable)(nil)): if v := coll.Variables[name]; v != nil { assignedVars[name] = true return v, nil } return nil, fmt.Errorf("missing variable %q", name) default: return nil, fmt.Errorf("unsupported type %s", typ) } } if err := assignValues(to, getValue); err != nil { return err } // Finalize ownership transfer for p := range assignedProgs { delete(coll.Programs, p) } for m := range assignedMaps { delete(coll.Maps, m) } for s := range assignedVars { delete(coll.Variables, s) } return nil } // Close frees all maps and programs associated with the collection. // // The collection mustn't be used afterwards. func (coll *Collection) Close() { for _, prog := range coll.Programs { prog.Close() } for _, m := range coll.Maps { m.Close() } } // DetachMap removes the named map from the Collection. // // This means that a later call to Close() will not affect this map. // // Returns nil if no map of that name exists. func (coll *Collection) DetachMap(name string) *Map { m := coll.Maps[name] delete(coll.Maps, name) return m } // DetachProgram removes the named program from the Collection. // // This means that a later call to Close() will not affect this program. // // Returns nil if no program of that name exists. func (coll *Collection) DetachProgram(name string) *Program { p := coll.Programs[name] delete(coll.Programs, name) return p } // structField represents a struct field containing the ebpf struct tag. type structField struct { reflect.StructField value reflect.Value } // ebpfFields extracts field names tagged with 'ebpf' from a struct type. // Keep track of visited types to avoid infinite recursion. func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) { if visited == nil { visited = make(map[reflect.Type]bool) } structType := structVal.Type() if structType.Kind() != reflect.Struct { return nil, fmt.Errorf("%s is not a struct", structType) } if visited[structType] { return nil, fmt.Errorf("recursion on type %s", structType) } fields := make([]structField, 0, structType.NumField()) for i := 0; i < structType.NumField(); i++ { field := structField{structType.Field(i), structVal.Field(i)} // If the field is tagged, gather it and move on. name := field.Tag.Get("ebpf") if name != "" { fields = append(fields, field) continue } // If the field does not have an ebpf tag, but is a struct or a pointer // to a struct, attempt to gather its fields as well. var v reflect.Value switch field.Type.Kind() { case reflect.Ptr: if field.Type.Elem().Kind() != reflect.Struct { continue } if field.value.IsNil() { return nil, fmt.Errorf("nil pointer to %s", structType) } // Obtain the destination type of the pointer. v = field.value.Elem() case reflect.Struct: // Reference the value's type directly. v = field.value default: continue } inner, err := ebpfFields(v, visited) if err != nil { return nil, fmt.Errorf("field %s: %w", field.Name, err) } fields = append(fields, inner...) } return fields, nil } // assignValues attempts to populate all fields of 'to' tagged with 'ebpf'. // // getValue is called for every tagged field of 'to' and must return the value // to be assigned to the field with the given typ and name. func assignValues(to interface{}, getValue func(typ reflect.Type, name string) (interface{}, error)) error { toValue := reflect.ValueOf(to) if toValue.Type().Kind() != reflect.Ptr { return fmt.Errorf("%T is not a pointer to struct", to) } if toValue.IsNil() { return fmt.Errorf("nil pointer to %T", to) } fields, err := ebpfFields(toValue.Elem(), nil) if err != nil { return err } type elem struct { // Either *Map or *Program typ reflect.Type name string } assigned := make(map[elem]string) for _, field := range fields { // Get string value the field is tagged with. tag := field.Tag.Get("ebpf") if strings.Contains(tag, ",") { return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name) } // Check if the eBPF object with the requested // type and tag was already assigned elsewhere. e := elem{field.Type, tag} if af := assigned[e]; af != "" { return fmt.Errorf("field %s: object %q was already assigned to %s", field.Name, tag, af) } // Get the eBPF object referred to by the tag. value, err := getValue(field.Type, tag) if err != nil { return fmt.Errorf("field %s: %w", field.Name, err) } if !field.value.CanSet() { return fmt.Errorf("field %s: can't set value", field.Name) } field.value.Set(reflect.ValueOf(value)) assigned[e] = field.Name } return nil } golang-github-cilium-ebpf-0.21.0+ds1/collection_other.go000066400000000000000000000002751520243672000230660ustar00rootroot00000000000000//go:build !windows package ebpf import "github.com/cilium/ebpf/internal" func loadCollectionFromNativeImage(_ string) (*Collection, error) { return nil, internal.ErrNotSupportedOnOS } golang-github-cilium-ebpf-0.21.0+ds1/collection_test.go000066400000000000000000000435301520243672000227250ustar00rootroot00000000000000package ebpf import ( "encoding/binary" "errors" "fmt" "io" "os" "reflect" "slices" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/testmain" ) func TestMain(m *testing.M) { testmain.Run(m) } func TestCollectionSpecNotModified(t *testing.T) { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "my-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, ".rodata": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, Flags: 0, // Loader sets BPF_F_MMAPABLE. Contents: []MapKV{{uint32(0), uint32(1)}}, }, }, Programs: map[string]*ProgramSpec{ "test": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R1, 0, asm.DWord).WithReference(".rodata"), asm.LoadImm(asm.R1, 0, asm.DWord).WithReference("my-map"), asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } orig := spec.Copy() coll := mustNewCollection(t, spec, nil) qt.Assert(t, qt.CmpEquals(orig, spec, csCmpOpts)) for name := range spec.Maps { qt.Assert(t, qt.IsNotNil(coll.Maps[name])) } for name := range spec.Programs { qt.Assert(t, qt.IsNotNil(coll.Programs[name])) } } func TestCollectionSpecCopy(t *testing.T) { ms := &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, } cs := &CollectionSpec{ map[string]*MapSpec{"my-map": ms}, map[string]*ProgramSpec{ "test": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadMapPtr(asm.R1, 0), asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, map[string]*VariableSpec{ "my-var": { Name: "my-var", SectionName: "my-map", Offset: 0, }, }, &btf.Spec{}, binary.LittleEndian, } qt.Check(t, qt.IsNil((*CollectionSpec)(nil).Copy())) qt.Assert(t, testutils.IsDeepCopy(cs.Copy(), cs)) } // Load key "0" from a map called "test-map" and return the value. var loadKeyFromMapProgramSpec = &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ // R1 map asm.LoadMapPtr(asm.R1, 0).WithReference("test-map"), // R2 key asm.Mov.Reg(asm.R2, asm.R10), asm.Add.Imm(asm.R2, -4), asm.StoreImm(asm.R2, 0, 0, asm.Word), // Lookup map[0] asm.FnMapLookupElem.Call(), asm.JEq.Imm(asm.R0, 0, "error"), asm.LoadMem(asm.R0, asm.R0, 0, asm.Word), asm.Ja.Label("ret"), // Windows doesn't allow directly using R0 result from FnMapLookupElem. asm.Mov.Imm(asm.R0, 0).WithSymbol("error"), asm.Return().WithSymbol("ret"), }, } func TestCollectionSpecMapReplacements(t *testing.T) { cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "test-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "test-prog": loadKeyFromMapProgramSpec.Copy(), }, } // Replace the map with another one newMap := mustNewMap(t, cs.Maps["test-map"], nil) err := newMap.Put(uint32(0), uint32(2)) if err != nil { t.Fatal(err) } coll := mustNewCollection(t, cs, &CollectionOptions{ MapReplacements: map[string]*Map{ "test-map": newMap, }, }) ret := mustRun(t, coll.Programs["test-prog"], nil) if ret != 2 { t.Fatal("new / override map not used") } // Check that newMap isn't closed when the collection is closed coll.Close() err = newMap.Put(uint32(0), uint32(3)) if err != nil { t.Fatalf("failed to update replaced map: %s", err) } } func TestCollectionSpecMapReplacements_NonExistingMap(t *testing.T) { cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "test-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, } // Override non-existing map newMap := mustNewMap(t, cs.Maps["test-map"], nil) coll, err := newCollection(t, cs, &CollectionOptions{ MapReplacements: map[string]*Map{ "non-existing-map": newMap, }, }) if err == nil { coll.Close() t.Fatal("Overriding a non existing map did not fail") } } func TestCollectionSpecMapReplacements_SpecMismatch(t *testing.T) { cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "test-map": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, } // Override map with mismatching spec newMap := mustNewMap(t, &MapSpec{ Type: Array, KeySize: 4, ValueSize: 8, // this is different MaxEntries: 1, }, nil) coll, err := newCollection(t, cs, &CollectionOptions{ MapReplacements: map[string]*Map{ "test-map": newMap, }, }) if err == nil { coll.Close() t.Fatal("Overriding a map with a mismatching spec did not fail") } if !errors.Is(err, ErrMapIncompatible) { t.Fatalf("Overriding a map with a mismatching spec failed with the wrong error") } } func TestMapReplacementsDataSections(t *testing.T) { // In some circumstances, it can be useful to share data sections between // Collections, for example to hold a ready/pause flag or some metrics. // Test read-only maps for good measure. file := testutils.NativeFile(t, "testdata/loader-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var objs struct { Data *Map `ebpf:".data"` ROData *Map `ebpf:".rodata"` } mustLoadAndAssign(t, spec, &objs, nil) defer objs.Data.Close() defer objs.ROData.Close() mustLoadAndAssign(t, spec, &objs, &CollectionOptions{ MapReplacements: map[string]*Map{ ".data": objs.Data, ".rodata": objs.ROData, }, }) qt.Assert(t, qt.IsNil(objs.Data.Close())) qt.Assert(t, qt.IsNil(objs.ROData.Close())) } func TestCollectionSpec_LoadAndAssign_LazyLoading(t *testing.T) { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "valid": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, "bogus": { Type: Array, MaxEntries: 0, }, }, Programs: map[string]*ProgramSpec{ "valid": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, "bogus": { Type: SocketFilter, Instructions: asm.Instructions{ // Undefined return value is rejected asm.Return(), }, License: "MIT", }, }, } var objs struct { Prog *Program `ebpf:"valid"` Map *Map `ebpf:"valid"` } mustLoadAndAssign(t, spec, &objs, nil) defer objs.Prog.Close() defer objs.Map.Close() if objs.Prog == nil { t.Error("Program is nil") } if objs.Map == nil { t.Error("Map is nil") } } func TestCollectionSpecAssign(t *testing.T) { var specs struct { Program *ProgramSpec `ebpf:"prog1"` Map *MapSpec `ebpf:"map1"` Variable *VariableSpec `ebpf:"var1"` } mapSpec := &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, } progSpec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", } cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": mapSpec, }, Programs: map[string]*ProgramSpec{ "prog1": progSpec, }, Variables: map[string]*VariableSpec{ "var1": {}, }, } if err := cs.Assign(&specs); err != nil { t.Fatal("Can't assign spec:", err) } if specs.Program != progSpec { t.Fatalf("Expected Program to be %p, got %p", progSpec, specs.Program) } if specs.Map != mapSpec { t.Fatalf("Expected Map to be %p, got %p", mapSpec, specs.Map) } if err := cs.Assign(new(int)); err == nil { t.Fatal("Assign allows to besides *struct") } if err := cs.Assign(new(struct{ Foo int })); err != nil { t.Fatal("Assign doesn't ignore untagged fields") } unexported := new(struct { foo *MapSpec `ebpf:"map1"` }) if err := cs.Assign(unexported); err == nil { t.Error("Assign should return an error on unexported fields") } } func TestNewCollectionFdLeak(t *testing.T) { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, // 8 byte value will cause m.finalize to fail. Contents: []MapKV{{uint32(0), uint64(0)}}, }, }, } _, err := newCollection(t, spec, nil) qt.Assert(t, qt.IsNotNil(err)) } func TestAssignValues(t *testing.T) { zero := func(t reflect.Type, name string) (interface{}, error) { return reflect.Zero(t).Interface(), nil } type t1 struct { Bar int `ebpf:"bar"` } type t2 struct { t1 Foo int `ebpf:"foo"` } type t2ptr struct { *t1 Foo int `ebpf:"foo"` } invalid := []struct { name string to interface{} }{ {"non-struct", 1}, {"non-pointer struct", t1{}}, {"pointer to non-struct", new(int)}, {"embedded nil pointer", &t2ptr{}}, {"unexported field", new(struct { foo int `ebpf:"foo"` })}, {"identical tag", new(struct { Foo1 int `ebpf:"foo"` Foo2 int `ebpf:"foo"` })}, } for _, testcase := range invalid { t.Run(testcase.name, func(t *testing.T) { if err := assignValues(testcase.to, zero); err == nil { t.Fatal("assignValues didn't return an error") } else { t.Log(err) } }) } valid := []struct { name string to interface{} }{ {"pointer to struct", new(t1)}, {"embedded struct", new(t2)}, {"embedded struct pointer", &t2ptr{t1: new(t1)}}, {"untagged field", new(struct{ Foo int })}, } for _, testcase := range valid { t.Run(testcase.name, func(t *testing.T) { if err := assignValues(testcase.to, zero); err != nil { t.Fatal("assignValues returned", err) } }) } } func TestCollectionAssign(t *testing.T) { var objs struct { Program *Program `ebpf:"prog1"` Map *Map `ebpf:"map1"` } cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "prog1": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } coll := mustNewCollection(t, cs, nil) qt.Assert(t, qt.IsNil(coll.Assign(&objs))) defer objs.Program.Close() defer objs.Map.Close() // Check that objs has received ownership of map and prog qt.Assert(t, qt.IsTrue(objs.Program.FD() >= 0)) qt.Assert(t, qt.IsTrue(objs.Map.FD() >= 0)) // Check that the collection has lost ownership qt.Assert(t, qt.IsNil(coll.Programs["prog1"])) qt.Assert(t, qt.IsNil(coll.Maps["map1"])) } func TestCollectionAssignFail(t *testing.T) { // `map2` does not exist var objs struct { Program *Program `ebpf:"prog1"` Map *Map `ebpf:"map2"` } cs := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "prog1": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } coll := mustNewCollection(t, cs, nil) qt.Assert(t, qt.IsNotNil(coll.Assign(&objs))) // Check that the collection has retained ownership qt.Assert(t, qt.IsNotNil(coll.Programs["prog1"])) qt.Assert(t, qt.IsNotNil(coll.Maps["map1"])) } func TestIncompleteLoadAndAssign(t *testing.T) { spec := &CollectionSpec{ Programs: map[string]*ProgramSpec{ "valid": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, "invalid": { Type: SocketFilter, Instructions: asm.Instructions{ asm.Return(), }, License: "MIT", }, }, } s := struct { // Assignment to Valid should execute and succeed. Valid *Program `ebpf:"valid"` // Assignment to Invalid should fail and cause Valid's fd to be closed. Invalid *Program `ebpf:"invalid"` }{} if err := loadAndAssign(t, spec, &s, nil); err == nil { t.Fatal("expected error loading invalid ProgramSpec") } if s.Valid == nil { t.Fatal("expected valid prog to be non-nil") } if fd := s.Valid.FD(); fd != -1 { t.Fatal("expected valid prog to have closed fd -1, got:", fd) } if s.Invalid != nil { t.Fatal("expected invalid prog to be nil due to never being assigned") } } func BenchmarkNewCollection(b *testing.B) { file := testutils.NativeFile(b, "testdata/loader-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { b.Fatal(err) } for _, m := range spec.Maps { m.Pinning = PinNone } spec = fixupCollectionSpec(spec) b.ReportAllocs() for b.Loop() { coll, err := NewCollection(spec) if err != nil { b.Fatal(err) } coll.Close() } } func BenchmarkNewCollectionManyProgs(b *testing.B) { file := testutils.NativeFile(b, "testdata/manyprogs-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { b.Fatal(err) } spec = fixupCollectionSpec(spec) b.ReportAllocs() for b.Loop() { coll, err := NewCollection(spec) if err != nil { b.Fatal(err) } coll.Close() } } func BenchmarkLoadCollectionManyProgs(b *testing.B) { file, err := os.Open(testutils.NativeFile(b, "testdata/manyprogs-%s.elf")) qt.Assert(b, qt.IsNil(err)) defer file.Close() b.ReportAllocs() for b.Loop() { _, err := file.Seek(0, io.SeekStart) if err != nil { b.Fatal(err) } _, err = LoadCollectionSpecFromReader(file) if err != nil { b.Fatal(err) } } } func ExampleCollectionSpec_Assign() { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "prog1": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } type maps struct { Map *MapSpec `ebpf:"map1"` } var specs struct { maps Program *ProgramSpec `ebpf:"prog1"` } if err := spec.Assign(&specs); err != nil { panic(err) } fmt.Println(specs.Program.Type) fmt.Println(specs.Map.Type) // Output: SocketFilter // Array } func ExampleCollectionSpec_LoadAndAssign() { spec := &CollectionSpec{ Maps: map[string]*MapSpec{ "map1": { Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, Programs: map[string]*ProgramSpec{ "prog1": { Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }, }, } var objs struct { Program *Program `ebpf:"prog1"` Map *Map `ebpf:"map1"` } if err := spec.LoadAndAssign(&objs, nil); err != nil { panic(err) } defer objs.Program.Close() defer objs.Map.Close() } func TestStructOpsMapSpecSimpleLoadAndAssign(t *testing.T) { requireTestmodOps(t) makeProg := func(attachTo string) map[string]*ProgramSpec { return map[string]*ProgramSpec{ "test_1": { Name: "test_1", Type: StructOps, AttachTo: attachTo, License: "GPL", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }, } } funcPtr := &btf.Pointer{ Target: &btf.FuncProto{ Return: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}, }, } type testCase struct { name string withProg bool attachTo string valueType *btf.Struct valueBytes []byte } cases := []testCase{ { name: "ops_with_data", withProg: true, attachTo: "bpf_testmod_ops:test_1", valueType: &btf.Struct{ Name: "bpf_testmod_ops", Size: 16, Members: []btf.Member{ { Name: "test_1", Type: funcPtr, Offset: 0, }, { Name: "data", Type: &btf.Int{Name: "int", Size: 4}, Offset: 64, // bits }, }, }, valueBytes: []byte{ // test_1 func ptr (8B) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data (4B) + padding (4B) 0xde, 0xed, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, }, }, { name: "ops_only_func", withProg: true, attachTo: "bpf_testmod_ops2:test_1", valueType: &btf.Struct{ Name: "bpf_testmod_ops2", Size: 8, Members: []btf.Member{ { Name: "test_1", Type: funcPtr, Offset: 0, }, }, }, valueBytes: []byte{ // test_1 func ptr (8B) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, }, { name: "ops_empty_value", withProg: false, valueType: &btf.Struct{ Name: "bpf_testmod_ops2", Size: 0, Members: []btf.Member{}, }, valueBytes: []byte{}, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { spec := &CollectionSpec{ Programs: map[string]*ProgramSpec{}, Maps: map[string]*MapSpec{ "testmod_ops": { Name: "testmod_ops", Type: StructOpsMap, Flags: sys.BPF_F_LINK, Key: &btf.Int{Size: 4}, KeySize: 4, Value: c.valueType, MaxEntries: 1, Contents: []MapKV{ { Key: uint32(0), Value: slices.Clone(c.valueBytes), }, }, }, }, } if c.withProg { spec.Programs = makeProg(c.attachTo) } coll := mustNewCollection(t, spec, nil) for name := range spec.Maps { qt.Assert(t, qt.IsNotNil(coll.Maps[name])) } for name := range spec.Programs { qt.Assert(t, qt.IsNotNil(coll.Programs[name])) } }) } } func TestLinkedELF(t *testing.T) { spec, err := LoadCollectionSpec("testdata/linked-el.elf") qt.Assert(t, qt.IsNil(err)) // Require all maps that won during linking to have a MaxEntries of 1. for name, m := range spec.Maps { qt.Assert(t, qt.Equals(m.MaxEntries, 1), qt.Commentf(name)) } // Require all programs that won during linking to return 0 when executed. // Programs that should be overridden during linking should return their line // numbers. coll := mustNewCollection(t, spec, nil) for name, prog := range coll.Programs { res := mustRun(t, prog, nil) qt.Assert(t, qt.Equals(res, 0), qt.Commentf(name)) } } golang-github-cilium-ebpf-0.21.0+ds1/collection_windows.go000066400000000000000000000062741520243672000234440ustar00rootroot00000000000000package ebpf import ( "errors" "fmt" "unsafe" "github.com/cilium/ebpf/internal/efw" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) func loadCollectionFromNativeImage(file string) (_ *Collection, err error) { mapFds := make([]efw.FD, 32) programFds := make([]efw.FD, 32) var maps map[string]*Map var programs map[string]*Program defer func() { if err == nil { return } for _, fd := range append(mapFds, programFds...) { // efW never uses fd 0. if fd != 0 { _ = efw.EbpfCloseFd(int(fd)) } } for _, m := range maps { _ = m.Close() } for _, p := range programs { _ = p.Close() } }() nMaps, nPrograms, err := efw.EbpfObjectLoadNativeFds(file, mapFds, programFds) if errors.Is(err, efw.EBPF_NO_MEMORY) && (nMaps > len(mapFds) || nPrograms > len(programFds)) { mapFds = make([]efw.FD, nMaps) programFds = make([]efw.FD, nPrograms) nMaps, nPrograms, err = efw.EbpfObjectLoadNativeFds(file, mapFds, programFds) } if err != nil { return nil, err } mapFds = mapFds[:nMaps] programFds = programFds[:nPrograms] // The maximum length of a name is only 16 bytes on Linux, longer names // are truncated. This is not a problem when loading from an ELF, since // we get the full object name from the symbol table. // When loading a native image we do not have this luxury. Use an efW native // API to retrieve up to 64 bytes of the object name. maps = make(map[string]*Map, len(mapFds)) for _, raw := range mapFds { fd, err := sys.NewFD(int(raw)) if err != nil { return nil, err } m, mapErr := newMapFromFD(fd) if mapErr != nil { _ = fd.Close() return nil, mapErr } var efwMapInfo efw.BpfMapInfo size := uint32(unsafe.Sizeof(efwMapInfo)) _, err = efw.EbpfObjectGetInfoByFd(m.FD(), unsafe.Pointer(&efwMapInfo), &size) if err != nil { _ = m.Close() return nil, err } if size >= uint32(unsafe.Offsetof(efwMapInfo.Name)+unsafe.Sizeof(efwMapInfo.Name)) { m.name = unix.ByteSliceToString(efwMapInfo.Name[:]) } if m.name == "" { _ = m.Close() return nil, fmt.Errorf("unnamed map") } if _, ok := maps[m.name]; ok { return nil, fmt.Errorf("duplicate map with the same name: %s", m.name) } maps[m.name] = m } programs = make(map[string]*Program, len(programFds)) for _, raw := range programFds { fd, err := sys.NewFD(int(raw)) if err != nil { return nil, err } program, err := newProgramFromFD(fd) if err != nil { _ = fd.Close() return nil, err } var efwProgInfo efw.BpfProgInfo size := uint32(unsafe.Sizeof(efwProgInfo)) _, err = efw.EbpfObjectGetInfoByFd(program.FD(), unsafe.Pointer(&efwProgInfo), &size) if err != nil { _ = program.Close() return nil, err } if size >= uint32(unsafe.Offsetof(efwProgInfo.Name)+unsafe.Sizeof(efwProgInfo.Name)) { program.name = unix.ByteSliceToString(efwProgInfo.Name[:]) } if program.name == "" { _ = program.Close() return nil, fmt.Errorf("unnamed program") } if _, ok := programs[program.name]; ok { _ = program.Close() return nil, fmt.Errorf("duplicate program with the same name: %s", program.name) } programs[program.name] = program } return &Collection{programs, maps, nil}, nil } golang-github-cilium-ebpf-0.21.0+ds1/collection_windows_test.go000066400000000000000000000022231520243672000244710ustar00rootroot00000000000000package ebpf import ( "path/filepath" "sort" "testing" "github.com/go-quicktest/qt" ) func TestLoadNativeImage(t *testing.T) { for _, tc := range []struct { file string maps []string programs []string }{ { "testdata/windows/cgroup_sock_addr.sys", []string{ "egress_connection_policy_map", "ingress_connection_policy_map", "socket_cookie_map", }, []string{ "authorize_connect4", "authorize_connect6", "authorize_recv_accept4", "authorize_recv_accept6", }, }, } { t.Run(filepath.Base(tc.file), func(t *testing.T) { coll, err := LoadCollection(tc.file) qt.Assert(t, qt.IsNil(err)) defer coll.Close() var mapNames []string for name, obj := range coll.Maps { qt.Assert(t, qt.Equals(obj.name, name)) mapNames = append(mapNames, name) } sort.Strings(mapNames) qt.Assert(t, qt.DeepEquals(mapNames, tc.maps)) var programNames []string for name, obj := range coll.Programs { qt.Assert(t, qt.Equals(obj.name, name)) programNames = append(programNames, name) } sort.Strings(programNames) qt.Assert(t, qt.DeepEquals(programNames, tc.programs)) }) } } golang-github-cilium-ebpf-0.21.0+ds1/cpu.go000066400000000000000000000006211520243672000203140ustar00rootroot00000000000000package ebpf // PossibleCPU returns the max number of CPUs a system may possibly have // Logical CPU numbers must be of the form 0-n func PossibleCPU() (int, error) { return possibleCPU() } // MustPossibleCPU is a helper that wraps a call to PossibleCPU and panics if // the error is non-nil. func MustPossibleCPU() int { cpus, err := PossibleCPU() if err != nil { panic(err) } return cpus } golang-github-cilium-ebpf-0.21.0+ds1/cpu_other.go000066400000000000000000000003401520243672000215130ustar00rootroot00000000000000//go:build !windows package ebpf import ( "sync" "github.com/cilium/ebpf/internal/linux" ) var possibleCPU = sync.OnceValues(func() (int, error) { return linux.ParseCPUsFromFile("/sys/devices/system/cpu/possible") }) golang-github-cilium-ebpf-0.21.0+ds1/cpu_test.go000066400000000000000000000003051520243672000213520ustar00rootroot00000000000000package ebpf import ( "testing" "github.com/go-quicktest/qt" ) func TestPossibleCPU(t *testing.T) { num, err := PossibleCPU() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(num > 0)) } golang-github-cilium-ebpf-0.21.0+ds1/cpu_windows.go000066400000000000000000000003131520243672000220640ustar00rootroot00000000000000package ebpf import ( "sync" "golang.org/x/sys/windows" ) var possibleCPU = sync.OnceValues(func() (int, error) { return int(windows.GetMaximumProcessorCount(windows.ALL_PROCESSOR_GROUPS)), nil }) golang-github-cilium-ebpf-0.21.0+ds1/doc.go000066400000000000000000000025161520243672000202770ustar00rootroot00000000000000// Package ebpf is a toolkit for working with eBPF programs. // // eBPF programs are small snippets of code which are executed directly // in a VM in the Linux kernel, which makes them very fast and flexible. // Many Linux subsystems now accept eBPF programs. This makes it possible // to implement highly application specific logic inside the kernel, // without having to modify the actual kernel itself. // // This package is designed for long-running processes which // want to use eBPF to implement part of their application logic. It has no // run-time dependencies outside of the library and the Linux kernel itself. // eBPF code should be compiled ahead of time using clang, and shipped with // your application as any other resource. // // Use the link subpackage to attach a loaded program to a hook in the kernel. // // Note that losing all references to Map and Program resources will cause // their underlying file descriptors to be closed, potentially removing those // objects from the kernel. Always retain a reference by e.g. deferring a // Close() of a Collection or LoadAndAssign object until application exit. // // Special care needs to be taken when handling maps of type ProgramArray, // as the kernel erases its contents when the last userspace or bpffs // reference disappears, regardless of the map being in active use. package ebpf golang-github-cilium-ebpf-0.21.0+ds1/docs/000077500000000000000000000000001520243672000201275ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/.gitignore000066400000000000000000000000621520243672000221150ustar00rootroot00000000000000# Python __pycache__ # Build output build/ site/ golang-github-cilium-ebpf-0.21.0+ds1/docs/Makefile000066400000000000000000000013631520243672000215720ustar00rootroot00000000000000build: pipenv @# Run a production build of the documentation. Strict mode makes warnings fatal. pipenv run mkdocs build --strict @# Build main packages, discarding build output. go build -v ./... @# Build _test.go files containing Doc* functions, don't execute tests. go test -c -o /dev/null ./... >/dev/null preview: pipenv pipenv run mkdocs serve shell: pipenv @echo "pipenv shell" @exec pipenv shell pipenv: ifeq (, $(shell command -v pipenv 2> /dev/null)) $(error "pipenv is not installed, exiting..") endif @# Ensure a venv and install dependencies from Pipfile.lock. Buffer stdio @# and display it on error as pipenv uses stdin and stderr arbitrarily. @echo "pipenv sync" @out=`pipenv sync 2>&1` || echo "$${out}" .PHONY: pipenv golang-github-cilium-ebpf-0.21.0+ds1/docs/Pipfile000066400000000000000000000005341520243672000214440ustar00rootroot00000000000000[[source]] url = "https://pypi.org/simple" verify_ssl = true name = "pypi" [packages] mkdocs = "*" pymdown-extensions = "*" mkdocs-material = "*" mkdocs-macros-plugin = "*" mkdocs-git-revision-date-localized-plugin = "*" mkdocs-git-authors-plugin = "*" [dev-packages] [requires] # Whatever Netlify's Ubuntu version uses. python_version = "3.13" golang-github-cilium-ebpf-0.21.0+ds1/docs/Pipfile.lock000066400000000000000000001427721520243672000224060ustar00rootroot00000000000000{ "_meta": { "hash": { "sha256": "d4b7168319b4861831784a317748437b6bcf28f91c23b400c6457d939cc9998b" }, "pipfile-spec": 6, "requires": { "python_version": "3.13" }, "sources": [ { "name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": true } ] }, "default": { "babel": { "hashes": [ "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35" ], "markers": "python_version >= '3.8'", "version": "==2.18.0" }, "backrefs": { "hashes": [ "sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be", "sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8", "sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b", "sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7", "sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90", "sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7", "sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49" ], "markers": "python_version >= '3.9'", "version": "==6.2" }, "certifi": { "hashes": [ "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7" ], "markers": "python_version >= '3.7'", "version": "==2026.2.25" }, "charset-normalizer": { "hashes": [ "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63", "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af", "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576", "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a", "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7", "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2", "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa", "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3", "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf", "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac", "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4", "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84", "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074", "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3", "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d", "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608" ], "markers": "python_version >= '3.7'", "version": "==3.4.4" }, "click": { "hashes": [ "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6" ], "markers": "python_version >= '3.10'", "version": "==8.3.1" }, "colorama": { "hashes": [ "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", "version": "==0.4.6" }, "ghp-import": { "hashes": [ "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343" ], "version": "==2.1.0" }, "gitdb": { "hashes": [ "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf" ], "markers": "python_version >= '3.7'", "version": "==4.0.12" }, "gitpython": { "hashes": [ "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058" ], "markers": "python_version >= '3.7'", "version": "==3.1.46" }, "hjson": { "hashes": [ "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75", "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89" ], "version": "==3.1.0" }, "idna": { "hashes": [ "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" ], "markers": "python_version >= '3.8'", "version": "==3.11" }, "importlib-metadata": { "hashes": [ "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7" ], "markers": "python_version >= '3.8'", "version": "==8.5.0" }, "jinja2": { "hashes": [ "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" ], "markers": "python_version >= '3.7'", "version": "==3.1.6" }, "markdown": { "hashes": [ "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36" ], "markers": "python_version >= '3.10'", "version": "==3.10.2" }, "markupsafe": { "hashes": [ "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50" ], "markers": "python_version >= '3.9'", "version": "==3.0.3" }, "mergedeep": { "hashes": [ "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307" ], "markers": "python_version >= '3.6'", "version": "==1.3.4" }, "mkdocs": { "hashes": [ "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e" ], "index": "pypi", "markers": "python_version >= '3.8'", "version": "==1.6.1" }, "mkdocs-get-deps": { "hashes": [ "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134" ], "markers": "python_version >= '3.8'", "version": "==0.2.0" }, "mkdocs-git-authors-plugin": { "hashes": [ "sha256:28421a99c3e872a8e205674bb80ec48524838243e5f59eaf9bd97df103e38901", "sha256:29d1973b2835663d79986fb756e02f1f0ff3fe35c278e993206bd3c550c205e4" ], "index": "pypi", "markers": "python_version >= '3.8'", "version": "==0.10.0" }, "mkdocs-git-revision-date-localized-plugin": { "hashes": [ "sha256:2b0239455cd84784dd87ac8dfc9253fe4b2dd35e102696f21b5d34e2175981c6", "sha256:b00fd36ed0f9b2326b1488fd8fa31bf2ce64e68c4aa60a9ce857f10719571903" ], "index": "pypi", "markers": "python_version >= '3.10'", "version": "==1.5.1" }, "mkdocs-macros-plugin": { "hashes": [ "sha256:12aa45ce7ecb7a445c66b9f649f3dd05e9b92e8af6bc65e4acd91d26f878c01f", "sha256:c10fabd812bf50f9170609d0ed518e54f1f0e12c334ac29141723a83c881dd6f" ], "index": "pypi", "markers": "python_version >= '3.8'", "version": "==1.5.0" }, "mkdocs-material": { "hashes": [ "sha256:37ebf7b4788c992203faf2e71900be3c197c70a4be9b0d72aed537b08a91dd9d", "sha256:e5f0a18319699da7e78c35e4a8df7e93537a888660f61a86bd773a7134798f22" ], "index": "pypi", "markers": "python_version >= '3.8'", "version": "==9.7.3" }, "mkdocs-material-extensions": { "hashes": [ "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31" ], "markers": "python_version >= '3.8'", "version": "==1.3.1" }, "packaging": { "hashes": [ "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529" ], "markers": "python_version >= '3.8'", "version": "==26.0" }, "paginate": { "hashes": [ "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591" ], "version": "==0.5.7" }, "pathspec": { "hashes": [ "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723" ], "markers": "python_version >= '3.9'", "version": "==1.0.4" }, "platformdirs": { "hashes": [ "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291" ], "markers": "python_version >= '3.10'", "version": "==4.9.2" }, "pygments": { "hashes": [ "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" ], "markers": "python_version >= '3.8'", "version": "==2.19.2" }, "pymdown-extensions": { "hashes": [ "sha256:39f4a020f40773f6b2ff31d2cd2546c2c04d0a6498c31d9c688d2be07e1767d5", "sha256:91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f" ], "index": "pypi", "markers": "python_version >= '3.9'", "version": "==10.21" }, "python-dateutil": { "hashes": [ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==2.9.0.post0" }, "pytz": { "hashes": [ "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" ], "version": "==2025.2" }, "pyyaml": { "hashes": [ "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6", "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295", "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b", "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369", "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4", "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f", "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3", "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" ], "markers": "python_version >= '3.8'", "version": "==6.0.3" }, "pyyaml-env-tag": { "hashes": [ "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff" ], "markers": "python_version >= '3.9'", "version": "==1.1" }, "regex": { "hashes": [ "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773", "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef", "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e", "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0", "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b", "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd", "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57", "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f", "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b", "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b", "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839", "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf", "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f", "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95", "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4", "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13", "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9", "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc", "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48", "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b", "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd", "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3", "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983", "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467", "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001", "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf", "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6", "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df", "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5", "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2", "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c", "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f", "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91" ], "markers": "python_version >= '3.8'", "version": "==2024.11.6" }, "requests": { "hashes": [ "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" ], "markers": "python_version >= '3.9'", "version": "==2.32.5" }, "six": { "hashes": [ "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==1.17.0" }, "smmap": { "hashes": [ "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e" ], "markers": "python_version >= '3.7'", "version": "==5.0.2" }, "super-collections": { "hashes": [ "sha256:0c8d8abacd9fad2c7c1c715f036c29f5db213f8cac65f24d45ecba12b4da187a", "sha256:291b74d26299e9051d69ad9d89e61b07b6646f86a57a2f5ab3063d206eee9c56" ], "markers": "python_version >= '3.8'", "version": "==0.6.2" }, "termcolor": { "hashes": [ "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5" ], "markers": "python_version >= '3.10'", "version": "==3.3.0" }, "urllib3": { "hashes": [ "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4" ], "markers": "python_version >= '3.9'", "version": "==2.6.3" }, "watchdog": { "hashes": [ "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2" ], "markers": "python_version >= '3.9'", "version": "==6.0.0" }, "zipp": { "hashes": [ "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29" ], "markers": "python_version >= '3.8'", "version": "==3.20.2" } }, "develop": {} } golang-github-cilium-ebpf-0.21.0+ds1/docs/README.md000066400000000000000000000013131520243672000214040ustar00rootroot00000000000000# epbf-go documentation The documentation project uses Pipenv to manage its dependencies, which will automatically create a Python virtualenv when invoked from this subdirectory. Follow your distribution's documentation for installing `pipenv`. You may also need `pyenv` to install a different Python version if your distribution doesn't provide the version specified in the `Pipfile`. Host a live preview of the documentation at http://127.0.0.1:8000: `make preview` Build the documentation, output to the site/ directory. This is a self-contained production copy that can be uploaded to hosting. `make build` To enter the virtualenv with all the documentation's Python dependencies installed: `make shell` golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/000077500000000000000000000000001520243672000210435ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/about.md000066400000000000000000000021101520243672000224710ustar00rootroot00000000000000The project was initially created in 2017 as [`newtools/ebpf`](https://github.com/newtools/ebpf) by a group of passionate developers wanting to bring the power eBPF to Go applications. It quickly gained traction within the Go community, especially for projects that couldn't or wouldn't build upon the CGo-based BCC bindings at the time (`gobpf`). Since its inception, {{ proj }} has seen remarkable growth and widespread adoption. It has become a fundamental building block for numerous open-source projects. Major industry players and forward-thinking startups have integrated the library into their technology stacks to combine the power and flexibility of eBPF with the iteration speed, runtime safety and ease of deployment provided by the Go language. {{ proj }} maintains a strong commitment to collaborating with the upstream Linux project, which ensures that it stays aligned with the latest advancements in the eBPF ecosystem and remains compatible with the evolving Linux kernel and its co-located BPF library, `libbpf`. Thank you for being a part of our :ebee-color: eBPF journey! golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/concepts/000077500000000000000000000000001520243672000226615ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/concepts/features.md000066400000000000000000000052061520243672000250240ustar00rootroot00000000000000# Feature Detection Feature detection allows applications to check which eBPF-related features are supported by the Linux kernel. This is useful for software that wants to be compatible with multiple kernel versions and lets developers tailor their code to use different eBPF features depending on what is supported by the running kernel. ## Usage In the `features` package, API calls follow a consistent pattern. The returned errors mean the following: - `nil` means the feature is supported. - {{ godoc('ErrNotSupported') }} means the feature is not supported. - Any other error suggests inconclusive detection, which could include false negatives. For example, here's using {{ godoc('features/HaveProgramType') }}: {{ go_example('DocDetectXDP', title="Detect kernel support for XDP programs") }} !!! note "" Feature detection results are cached to minimize overhead, except for inconclusive results. Subsequent calls to a conclusive probe will consistently return the same result without rerunning the probe logic. ## Limitations ### {{ godoc ('features/HaveProgramHelper') }} 1. Not all combinations of program types and helpers can be probed. Conclusively probing a BPF helper means successfully loading a generated BPF program. Certain program types like `LSM`, `StructOps` and `Tracing` are difficult to generate on-the-fly, as they depend on other components or symbols being present in the kernel, making the probes fragile. Instead, for these types, we don't rely on successfully loading a program, but we look for specific kernel error responses instead, such as `ENOTSUPP`. This indicates the program type is known, but our generated program was invalid (which is fine!). 2. This function only confirms the presence of the given BPF helper in the kernel. In cases where helpers themselves gain extra features in subsequent kernel releases, you'll have to write your own feature probe to test the particular combination of helper inputs you're looking for. Feel free to look at the implementation of package `features` for inspiration. ## Compared to `bpftool` Linux's command-line utility `bpftool` offers the `bpftool feature probe` subcommand for feature detection, inspiring the `features` package in {{ proj }}. That subcommand provides an extensive overview of eBPF-related features, issuing thousands of feature probes to identify kernel configuration options, and detect map types, program types, and helper functions. {{ proj }} aims to provide an equivalent set of feature probes, implemented in pure Go, to avoid a `bpftool` runtime dependency, and to allow users to probe only the exact features they need. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/concepts/global-variables.md000066400000000000000000000170051520243672000264140ustar00rootroot00000000000000{{ linux_version("5.2", "For all global variable-related BPF operations, the kernel needs to understand the BPF_PSEUDO_MAP_VALUE value in ldimm64 instructions. This is needed for direct, lookup-free map access." )}} Like typical C programs, BPF programs allow the use of global variables. These variables can be initialized from the BPF C code itself, or they can be modified by the loading user space application before handing it off to the kernel. The abstraction {{ proj }} provides to interact with global variables is the {{ godoc('VariableSpec') }}, found in the {{ godoc('CollectionSpec.Variables') }} field. This page describes how to declare variables in BPF C and how to interact with them in Go. ## Runtime Constants {{ linux_version("5.2", "Read-only maps and the BPF_MAP_FREEZE command are needed for implementing constant variables.") }} Global runtime constants are typically used for configuration values that influence the functionality of a BPF program. Think all sorts of network or hardware addresses for network filtering, or timeouts for rate limiting. The C compiler will reject any runtime modifications to these variables in the BPF program, like a typical const. Crucially, the BPF verifier will also perform dead code analysis if constants are used in if statements. If a condition is always true or false, it will remove unused code paths from the BPF program, reducing verification time and increasing runtime performance. This enables many features like portable kfuncs, allowing C code to refer to kfuncs that may not exist in some kernels, as long as those code paths are guaranteed not to execute at runtime. Similarly, this can be used to your advantage to disable code paths that are not needed in certain configurations, or would result in a verifier error on some kernels or in some contexts. :ebee-color: Consider the following C BPF program that reads a global constant and returns it: {{ c_example('variables_const', title='BPF C program declaring global constant const_u32') }} ??? warning "Why is `const_u32` declared `volatile`?" In short: without the `volatile` qualifier, the variable would be optimized away and not appear in the BPF object file, leaving us unable to modify it from our user space application. In this program, the compiler (in)correctly deduces two things about `const_u32`: it is never assigned a value, and it doesn't change over the course of the program. Implementation details aside, it will now assume that the return value of `const_example()` is always 0 and omit the variable from the ELF altogether. For BPF programs, it's common practice to declare all global variables that need to be accessed from user space as `volatile`, especially non-`const` globals. Doing so ensures the compiler reliably allocates them in a data section in the ELF. :simple-go: First, let's take a look at a full Go example that will comprise the majority of interactions with constants. In the example below, we'll load a BPF object from disk, pull out a variable, set its value and call the BPF program once with an empty context. Variations on this pattern will follow later. {{ go_example('DocVariablesSetConst', title='Go program modifying a const, loading and running the BPF program') }} 1. Any values passed into {{ godoc('VariableSpec.Set') }} must marshal to a fixed width. This behaviour is identical to {{ godoc('Map.Put') }} and friends. Using untyped integers is not supported since their size is platform dependent. We recommend the same approach in BPF C to keep data size predictable. 2. A 15-byte context is the minimum the kernel will accept for dry-running a BPF program. If your BPF program reads from its context, populating this slice is a great way of doing unit testing without setting up a live testing environment. ## Global Variables Non-const global variables are mutable and can be modified by both the BPF program and the user space application. They are typically used for keeping state like metrics, counters, rate limiting, etc. These variables can also be initialized from user space, much like their `const` counterparts, and can be both read and written to from the BPF program as well as the user space application. More on that in a future section. :ebee-color: The following C BPF program reads a global variable and returns it: {{ c_example('variables_global', title='BPF C program declaring global variable global_u16') }} ??? warning "Why is `global_u16` declared `volatile`?" Similar to `volatile const` in a prior example, `volatile` is used here to make compiler output more deterministic. Without it, the compiler may choose to optimize away a variable if it's never assigned to, not knowing its value is actually provided by user space. The `volatile` qualifier doesn't change the variable's semantics. ### Before Loading: Using VariableSpec For interacting with global variables before loading the BPF program into the kernel, use the methods on its {{ godoc('VariableSpec') }} found in {{ godoc('CollectionSpec.Variables') }} or injected using {{ godoc('LoadAndAssign') }}. This ensures the variable is populated before the BPF program has a chance to execute. :simple-go: In user space, initialize `global_u16` to 9000: {{ go_example('DocVariablesSetGlobalU16') }} Dry-running `global_example()` a few times results in the value increasing on every invocation: {{ go_example('DocVariablesSetGlobalRun') }} Once a CollectionSpec has been loaded into the kernel, further modifications to a VariableSpec are ineffectual. ### After Loading: Using Variable After loading the BPF program into the kernel, accessing global variables from user space can be done through the {{ godoc('Variable') }} abstraction. These can be injected into an object using {{ godoc('LoadAndAssign') }}, or found in the {{ godoc('Collection.Variables') }} field. :simple-go: Building on the previous example, read the incremented variable using {{ godoc('Variable.Get') }}: {{ go_example('DocVariablesGetGlobalU16') }} Modify the Variable at runtime using {{ godoc('Variable.Set') }}. ## Internal/Hidden Global Variables By default, all global variables described in an ELF's data sections are exposed through {{ godoc('CollectionSpec.Variables') }}. However, there may be cases where you don't want user space to interfere with a variable (either on purpose or by accident) and you want to keep the variable internal to the BPF program. {{ c_example('variables_hidden', title='BPF C program declaring internal global variable internal_var') }} The `__hidden` macro is found in Linux' `` as of version 5.13 and is defined as follows: ```c #define __hidden __attribute__((visibility("hidden"))) ``` This will cause the VariableSpec for `hidden_var` to not be included in the CollectionSpec. ## Static Global Variables With the introduction of `bpftool gen object`. BPF received a full-blown static linker, giving the `static` keyword for declaring objects local to a single .c file an actual semantic meaning. {{ proj }} follows the convention set by libbpf to not expose static variables to user space. In our case, this means that static variables are not included in the {{ godoc('CollectionSpec.Variables') }} field or emitted in bpf2go-generated code. The ELF loader has no way to differentiate function-scoped local variables (also not exposed) and static variables, since they're both marked with `LOCAL` linkage in the ELF. If you need to expose a variable to user space, drop the `static` keyword and declare it in the global scope of your BPF C program. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/concepts/loader.md000066400000000000000000000104321520243672000244510ustar00rootroot00000000000000# Loading Objects {{ proj }} ships an eBPF object (ELF) loader that aims to be compatible with the upstream libbpf and iproute2 (`tc`/`ip`) projects. An ELF is typically obtained by compiling a eBPF C program using the LLVM toolchain (`clang`). This page describes the journey from compiled eBPF ELF to resources in the kernel. This involves parsing the ELF into intermediate Go (Spec) types that can be modified and copied before loading them into the kernel. ```mermaid graph LR ELF --> ProgramSpec --> Program ELF --> Types ELF --> MapSpec --> Map Map & Program --> Links subgraph Collection Program & Map end subgraph CollectionSpec ProgramSpec & MapSpec & Types end ``` ## {{ godoc('CollectionSpec') }} A CollectionSpec represents eBPF objects extracted from an ELF, and can be obtained by calling {{ godoc('LoadCollectionSpec') }}. In the examples below, we declare a Map and Program in eBPF C, then load and inspect them using Go. Use the tabs to explore the Go and C counterparts below. === ":simple-go: Go" {{ go_example('DocLoadCollectionSpec', title='Parse ELF and inspect its CollectionSpec') | indent(4) }} !!! warning "" All of a Spec's attributes can be modified, and those modifications influence the resources created in the kernel. Be aware that doing so may invalidate any assumptions made by the compiler, resulting in maps or programs being rejected by the kernel. Proceed with caution. === ":ebee-color: eBPF C" {{ c_example('DocMyMapProgram', title='Declare a minimal map and a program') | indent(4) }} !!! tip "" See [Section Naming](section-naming.md) to learn about the use of the `SEC()` macro in the example above. ## {{ godoc('NewCollection') }} After parsing the ELF into a CollectionSpec, it can be loaded into the kernel using {{ godoc('NewCollection') }}, resulting in a {{ godoc('Collection') }}. {{ go_example('DocNewCollection') }} !!! note "" {{ godoc('Collection.Close') }} closes all Maps and Programs in the Collection. Interacting with any resources after `Close()` will return an error, since their underlying file descriptors will be closed. See [Object Lifecycle](object-lifecycle.md) to gain a better understanding of how {{ proj }} manages its resources and for best practices handling Maps and Programs. ## {{ godoc('CollectionSpec.LoadAndAssign', short=True) }} LoadAndAssign is a convenience API that can be used instead of `NewCollection`. It has two major benefits: - It automates pulling Maps and Programs out of a Collection. No more `#!go if m := coll.Maps["my_map"]; m == nil { return ... }`. - **Selective loading of Maps and Programs!** Only resources of interest and their dependencies are loaded into the kernel. Great for working with large CollectionSpecs that only need to be partially loaded. First, declare a struct that will receive pointers to a Map and a Program after loading them into the kernel. Give it a `#!go Close()` method to make cleanup easier. {{ go_example('DocLoadAndAssignObjs', title='Declare a custom struct myObjs') }} !!! note "" Use bpf2go if the preceding code snippet looks tedious. bpf2go can generate this kind of boilerplate code automatically and will make sure it stays in sync with your C code. Next, instantiate a variable of our newly-declared type and pass its pointer to `LoadAndAssign`. {{ go_example('DocLoadAndAssign', title='Pass a custom struct to LoadAndAssign') }} !!! warning "" If your use case requires dynamically renaming keys in CollectionSpec.Maps, you may need to use NewCollection instead. Map and Program names in struct tags are baked into the Go binary at compile time. ## Type Information (BTF) If an eBPF ELF was built with `clang -g`, it will automatically contain BTF type information. This information can be accessed programmatically through {{ godoc('CollectionSpec.Types') }}. Note that this field will be `nil` if the ELF was built without BTF. {{ go_example('DocBTFTypeByName') }} !!! note "" Many eBPF features rely on ELFs to be built with BTF, and there is little to be gained by opting out of it. `clang -g` also includes DWARF information in the ELF which can be safely removed with `llvm-strip`. eBPF does not rely on DWARF information. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/concepts/object-lifecycle.md000066400000000000000000000106541520243672000264140ustar00rootroot00000000000000!!! info "" This is an advanced topic and does not need to be fully understood in order to get started writing useful tools. If you find yourself debugging unexpectedly-detached programs, resource leaks, or you want to gain a deeper understanding of how eBPF objects are managed by {{ proj }}, this page should prove helpful. ## File Descriptors and Go Interacting with eBPF objects from user space is done using file descriptors. Counter-intuitively, 'file' descriptors are used as references to many types of kernel resources in modern Linux, not just files. In {{ proj }}, {{ godoc('Map') }}, {{ godoc('Program') }} and {{ godoc('link/Link') }} are all modeled around these underlying file descriptors. Go, being a garbage-collected language, automatically manages the lifecycle of Go objects. Keeping in line with the standard library's `os.File` and friends, eBPF resources in {{ proj }} were designed in a way so their underlying file descriptors are closed when their Go objects are garbage collected. This generally prevents runaway resource leaks, but is not without its drawbacks. This has subtle but important repercussions for BPF, since this means the Go runtime will call `Close()` on an object's underlying file descriptor if the object is no longer reachable by the garbage collector. For example, this can happen if an object is created in a function, but is not returned to the caller. One type of map, {{ godoc('ProgramArray') }}, is particularly sensitive to this. More about that in [Program Arrays](#program-arrays). ## Extending Object Lifetime ### Pinning Aside from file descriptors, BPF provides another method of creating references to eBPF objects: pinning. This is the concept of associating a file on a virtual file system (the BPF File System, bpffs for short) with a BPF resource like a Map, Program or Link. Pins can be organized into arbitrary directory structures, just like on any other file system. When the Go process exits, the pin will maintain a reference to the object, preventing it from being automatically destroyed. In this scenario, removing the pin using plain `rm` will remove the last reference, causing the kernel to destroy the object. If you're holding an active object in Go, you can also call {{ godoc('Map.Unpin') }}, {{ godoc('Program.Unpin') }} or {{ godoc('link/Link.Unpin') }} if the object was previously pinned. !!! warning Pins do **not** persist through a reboot! A common use case for pinning is sharing eBPF objects between processes. For example, one could create a Map from Go, pin it, and inspect it using `bpftool map dump pinned /sys/fs/bpf/my_map`. ### Attaching Attaching a Program to a hook acts as a reference to a Program, since the kernel needs to be able to execute the program's instructions at any point. For legacy reasons, some {{ godoc('link/Link') }} types don't support pinning. It is generally safe to assume these links will persist beyond the lifetime of the Go application. ## :warning: Program Arrays A {{ godoc('ProgramArray') }} is a Map type that holds references to other Programs. This allows programs to 'tail call' into other programs, useful for splitting up long and complex programs. Program Arrays have a unique property: they allow cyclic dependencies to be created between the Program Array and a Program (e.g. allowing programs to call into themselves).To avoid ending up with a set of programs loaded into the kernel that cannot be freed, the kernel maintains a hard rule: **Program Arrays require at least one open file descriptor or bpffs pin**. !!! warning If all user space/bpffs references are gone, **any tail calls into the array will fail**, but the Map itself will remain loaded as long as there are programs that use it. This property, combined with interactions with Go's garbage collector previously described in [File Descriptors and Go](#file-descriptors-and-go), is a great source of bugs. A few tips to handle this problem correctly: - Use {{ godoc('CollectionSpec.LoadAndAssign') }}. It will refuse to load the CollectionSpec if doing so would result in a Program Array without a userspace reference. - Pin Program Arrays if execution of your eBPF code needs to continue past the lifetime of your Go application, e.g. for upgrades or short-lived CLI tools. - Retain references to the Map at all times in long-running applications. Note that `#!go defer m.Close()` makes Go retain a reference until the end of the current scope. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/concepts/rlimit.md000066400000000000000000000070611520243672000245070ustar00rootroot00000000000000# Resource Limits Creating eBPF objects (Maps, Programs, even BTF blobs) requires kernel memory allocation. Before kernel version 5.11, the memory available to a process for creating eBPF objects was restricted by its `RLIMIT_MEMLOCK` rlimit value, visible through the `ulimit -l` command. Starting with [version 5.11](https://lore.kernel.org/bpf/20201201215900.3569844-1-guro@fb.com), the Linux kernel switched from rlimits to memory cgroup (memcg) accounting for managing memory limits on processes handling eBPF objects in the kernel. eBPF object allocations are tracked alongside regular allocations within the cgroup. Memory consumption and limits can be queried and set through cgroupfs, the same mechanism used for setting memory limits on containers. ## Purpose of package `rlimit` On kernels supporting memcg accounting, there's no need to manage `RLIMIT_MEMLOCK` for effectively using eBPF, as eBPF object allocations now count towards the cgroup memory limit instead. However, since many Linux distributions still ship pre-5.11 kernels, it's necessary to conditionally manage rlimit for kernels lacking memcg accounting for eBPF. To support writing portable Go tools that work across various kernel versions, the `rlimit` package was introduced. It encapsulates two behaviours: 1. As an **import side effect** of importing the package, it lowers the rlimit of the current process to induce a Map creation failure, then restores the original rlimit. 2. {{ godoc('rlimit/RemoveMemlock') }} conditionally increases `RLIMIT_MEMLOCK` to infinity based on the probe's result. If the kernel supports memcg accounting, this is a no-op. ## Usage Include this in your application: {{ go_example('DocRlimit', title="Remove RLIMIT_MEMLOCK if kernel lacks memcg accounting") }} !!! note "" You can call `RemoveMemlock()` multiple times if your program has multiple entry points or CLI subcommands. The rlimit operation will only execute once. ## Caveats ### Race Conditions The package was carefully designed with Go's runtime initialization semantics in mind, meaning only one `init()` will execute at a time across all packages, minimizing the risk of racing against other callers to `prlimit(2)` (which should hopefully be rare). The rlimit package first gets the process' current `RLIMIT_MEMLOCK` value, drops it to 0, attempts to create a BPF map, then finally resets the rlimit to the old value. It's important to note that this happens **before invoking** `RemoveMemlock()` and has two potential side effects: - On kernels before 5.11, other concurrent BPF object creations may fail due to insufficient memory being available while the rlimit is at 0. - Other Go packages interacting with `prlimit(2)` may interfere with this process, leading to a wrong `RLIMIT_MEMLOCK` value being read or restored. Please audit your code and dependencies for potential conflicts. ### Why does my application always create a Map on startup? !!! note "" The `rlimit` package is entirely optional and serves as a convenience feature. Since the package creates a Map from `init()`, there is currently no way to prevent your application from interacting with `bpf(2)`, even if `RemoveMemlock()` is never invoked or if none of your application's eBPF features remain disabled. We consider this a reasonable trade-off to provide maximum value for the majority of use cases. If this is not desirable, you can avoid using package `rlimit` altogether and increase the rlimit through other means like Docker's `--ulimit memlock=-1` flag or systemd's `LimitMEMLOCK=infinity` unit limit property. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/concepts/section-naming.md000066400000000000000000000374571520243672000261360ustar00rootroot00000000000000You may have seen the `SEC()` macro used around eBPF C code. This macro sends a hint to the compiler to place a symbol (a variable or function) in a specific section of the resulting eBPF object binary. Typically, program binaries for Unix-like systems are divided into so-called 'sections'. All sections have names, many of which are assigned special meaning. For example, `.text` is where [program text](https://en.wikipedia.org/wiki/Code_segment) (executable instructions) goes by default. Like common application binaries, eBPF also relies heavily on section naming to distinguish various parts of an application. As an example, the section name of an individual eBPF program determines its program type, affecting the way the program is verified by the kernel and defining what the program is allowed to do. ## Executable Linkable Format (ELF) Executable Linkable Format (ELF) is the standard application binary format for Linux. It is also used as the output format of LLVM's BPF backend. ELF binaries are typically [executed directly by the kernel](https://lwn.net/Articles/631631/), but for eBPF, a different approach is needed. eBPF programs are not executable in the traditional sense. They depend on a user space component that loads them, manages their resources, and can interact with their components. This is where projects such as libbpf and {{ proj }} come in. For compatibility reasons, {{ proj }} follows the section naming conventions established by libbpf, since we consider upstream decisions to be authoritative on this subject. There's also little reason to do things differently; section names are essentially considered an API. ??? tip "How do I explore an ELF's contents?" You can display an ELF's section table using `readelf -S `. For visualizing a program instructions or the contents of a map's data section, you'll need a tool from the LLVM toolchain: `llvm-objdump`. For example: `llvm-objdump -SD my_ebpf.o -j xdp`. This will limit output to the `xdp` section (see [Program Sections](#program-sections)), display corresponding source code lines if available using `-S`, and display disassembled instructions using `-D`. The same can be done for data sections like `.data` and `.rodata.` (see [Map Sections](#map-sections)). Also worth mentioning: display an eBPF object's BTF type information using `bpftool btf dump file my_object.o`. ## Section Prefixes To support encoding extra information into section names, a prefix convention using forward slashes `/` is used. For example, a Kprobe-type program meant to be attached to the `slub_flush` kernel symbol would be put into an ELF section called `kprobe/slub_flush`. ### Miscellaneous Sections `license` : In order to use certain BPF helpers in your program, it must be licensed under a GPL-compatible license. BPF programs licensing follows the same rules as kernel module licensing. This is explained in more detail in the Linux kernel's [BPF licensing documentation](https://docs.kernel.org/bpf/bpf_licensing.html#using-bpf-programs-in-the-linux-kernel). See the [`license_is_gpl_compatible`](https://elixir.bootlin.com/linux/v6.5.4/source/include/linux/license.h) function in the Linux source code or the [Module Licensing table](https://docs.kernel.org/process/license-rules.html#id1). This section must only contain the license string of the programs in the ELF. for example: `#!c char __license[] SEC("license") = "Dual MIT/GPL";`. `version` : **Deprecated.** Kernels <5.0 require this section to contain a value matching the kernel's `LINUX_VERSION_CODE` for Kprobe-type programs. Always omit this, {{ proj }} will populate this field automatically if needed. ### Map Sections `.maps` : This section is dedicated to BTF-style Map definitions. `maps` : **Deprecated.** This section is expected to only contain fixed-width `struct bpf_map_def` variables. Larger structs like iproute2's `struct bpf_elf_map` can also be used for backwards compatibility. Any extra bytes past the end of the size of a `struct bpf_map_def` are exposed by {{ godoc('MapSpec.Extra') }} and must be drained before attempting to create the Map. #### :material-head-cog: Advanced: Special Map Sections `.data*` : The LLVM BPF backend implements accesses to mutable global variables as direct Array Map accesses. Since a single BPF program can be executed concurrently as a result of the kernel processing packets and other events asynchronously, a data section and the global variables it represents are considered shared memory. Variables can be emitted to specific sections, like `#!c SEC(".data.foo") my_var = 123;`, as long as they match the `.data*` prefix. This can prove useful for isolating certain variables to well-known sections for Go code generation or custom variable rewriting logic. Global, non-hidden variables are emitted to {{ godoc('CollectionSpec.Variables') }}, where they can be modified before loading the CollectionSpec into the kernel. See [Global Variables](../concepts/global-variables.md) for instructions. `.rodata*` : Like `.data*`, but for constants. These become read-only after loading the CollectionSpec into the kernel, and are also exposed through {{ godoc('CollectionSpec.Variables') }}. `.bss` : Section emitted by the compiler when zero-initialized globals are present in the ELF. Is typically zero-length in the ELF, and initialized by {{ proj }} after loading. Also exposed through {{ godoc('CollectionSpec.Variables') }}. `.rel*` : Not exposed by {{ proj }}, only used behind the scenes. Relocation sections contain relocation records against their non-`.rel` prefixed counterparts. This is mainly used for fixing up BPF instructions referring to Maps and global variables. ### Program Sections Names of Program sections mainly define the program's {{ godoc('ProgramType') }}, but also its {{ godoc('AttachType') }} and {{ godoc('AttachFlags') }} are automatically set for convenience based on its section name. As described previously, section prefixes containing a forward slash `/` expect a second component to follow the slash. For example, a program in the `kprobe/slub_flush` section will automatically have its {{ godoc('ProgramSpec.AttachTo') }} field set to `slub_flush` to facilitate attaching the program later on. Additionally, the program's original full section name can be found in {{ godoc('ProgramSpec.SectionName') }}. !!! tip "" There's also [upstream libbpf documentation](https://docs.kernel.org/bpf/libbpf/program_types.html) for this. Not all of libbpf's program types may be supported by {{ proj }} yet. If a program type you require is missing, please file an issue or send a pull request! | Section (Prefix) | {{ godoc('ProgramType') }} | {{ godoc('AttachType') }} | {{ godoc('AttachFlags') }} | |:----------------------|:---------------------------|:---------------------------------|:---------------------------| | socket | SocketFilter | | | | sk_reuseport/migrate | SkReuseport | AttachSkReuseportSelectOrMigrate | | | sk_reuseport | SkReuseport | AttachSkReuseportSelect | | | kprobe/ | Kprobe | | | | uprobe/ | Kprobe | | | | kretprobe/ | Kprobe | | | | uretprobe/ | Kprobe | | | | tc | SchedCLS | | | | classifier | SchedCLS | | | | action | SchedACT | | | | tracepoint/ | TracePoint | | | | tp/ | TracePoint | | | | raw_tracepoint/ | RawTracepoint | | | | raw_tp/ | RawTracepoint | | | | raw_tracepoint.w/ | RawTracepointWritable | | | | raw_tp.w/ | RawTracepointWritable | | | | tp_btf/ | Tracing | AttachTraceRawTp | | | fentry/ | Tracing | AttachTraceFEntry | | | fmod_ret/ | Tracing | AttachModifyReturn | | | fexit/ | Tracing | AttachTraceFExit | | | fentry.s/ | Tracing | AttachTraceFEntry | BPF_F_SLEEPABLE | | fmod_ret.s/ | Tracing | AttachModifyReturn | BPF_F_SLEEPABLE | | fexit.s/ | Tracing | AttachTraceFExit | BPF_F_SLEEPABLE | | freplace/ | Extension | | | | lsm/ | LSM | AttachLSMMac | | | lsm.s/ | LSM | AttachLSMMac | BPF_F_SLEEPABLE | | iter/ | Tracing | AttachTraceIter | | | iter.s/ | Tracing | AttachTraceIter | BPF_F_SLEEPABLE | | syscall | Syscall | | | | xdp.frags/devmap | XDP | AttachXDPDevMap | BPF_F_XDP_HAS_FRAGS | | xdp/devmap | XDP | AttachXDPDevMap | | | xdp.frags/cpumap | XDP | AttachXDPCPUMap | BPF_F_XDP_HAS_FRAGS | | xdp/cpumap | XDP | AttachXDPCPUMap | | | xdp.frags | XDP | | BPF_F_XDP_HAS_FRAGS | | xdp | XDP | | | | perf_event | PerfEvent | | | | lwt_in | LWTIn | | | | lwt_out | LWTOut | | | | lwt_xmit | LWTXmit | | | | lwt_seg6local | LWTSeg6Local | | | | cgroup_skb/ingress | CGroupSKB | AttachCGroupInetIngress | | | cgroup_skb/egress | CGroupSKB | AttachCGroupInetEgress | | | cgroup/skb | CGroupSKB | | | | cgroup/sock_create | CGroupSock | AttachCGroupInetSockCreate | | | cgroup/sock_release | CGroupSock | AttachCgroupInetSockRelease | | | cgroup/sock | CGroupSock | AttachCGroupInetSockCreate | | | cgroup/post_bind4 | CGroupSock | AttachCGroupInet4PostBind | | | cgroup/post_bind6 | CGroupSock | AttachCGroupInet6PostBind | | | cgroup/dev | CGroupDevice | AttachCGroupDevice | | | sockops | SockOps | AttachCGroupSockOps | | | sk_skb/stream_parser | SkSKB | AttachSkSKBStreamParser | | | sk_skb/stream_verdict | SkSKB | AttachSkSKBStreamVerdict | | | sk_skb | SkSKB | | | | sk_msg | SkMsg | AttachSkMsgVerdict | | | lirc_mode2 | LircMode2 | AttachLircMode2 | | | flow_dissector | FlowDissector | AttachFlowDissector | | | cgroup/bind4 | CGroupSockAddr | AttachCGroupInet4Bind | | | cgroup/bind6 | CGroupSockAddr | AttachCGroupInet6Bind | | | cgroup/connect4 | CGroupSockAddr | AttachCGroupInet4Connect | | | cgroup/connect6 | CGroupSockAddr | AttachCGroupInet6Connect | | | cgroup/sendmsg4 | CGroupSockAddr | AttachCGroupUDP4Sendmsg | | | cgroup/sendmsg6 | CGroupSockAddr | AttachCGroupUDP6Sendmsg | | | cgroup/recvmsg4 | CGroupSockAddr | AttachCGroupUDP4Recvmsg | | | cgroup/recvmsg6 | CGroupSockAddr | AttachCGroupUDP6Recvmsg | | | cgroup/getpeername4 | CGroupSockAddr | AttachCgroupInet4GetPeername | | | cgroup/getpeername6 | CGroupSockAddr | AttachCgroupInet6GetPeername | | | cgroup/getsockname4 | CGroupSockAddr | AttachCgroupInet4GetSockname | | | cgroup/getsockname6 | CGroupSockAddr | AttachCgroupInet6GetSockname | | | cgroup/sysctl | CGroupSysctl | AttachCGroupSysctl | | | cgroup/getsockopt | CGroupSockopt | AttachCGroupGetsockopt | | | cgroup/setsockopt | CGroupSockopt | AttachCGroupSetsockopt | | | struct_ops+ | StructOps | | | | struct_ops.s+ | StructOps | | BPF_F_SLEEPABLE | | sk_lookup/ | SkLookup | AttachSkLookup | | | kprobe.multi | Kprobe | AttachTraceKprobeMulti | | | kretprobe.multi | Kprobe | AttachTraceKprobeMulti | | golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/contributing/000077500000000000000000000000001520243672000235525ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/contributing/architecture.md000066400000000000000000000077361520243672000265730ustar00rootroot00000000000000Architecture of the library === The bulk of the functionality of the library split across the `ebpf`, `btf` and `link` packages. Below is a diagram how the most important types relate to each other. The graph is in dependecy order, so an arrow from `Links` to `Map` can be read as "Link depends on Map". ```mermaid graph RL Program --> ProgramSpec --> ELF btf.Spec --> ELF Map --> MapSpec --> ELF Links --> Map & Program ProgramSpec -.-> btf.Spec MapSpec -.-> btf.Spec subgraph Collection Program & Map end subgraph CollectionSpec ProgramSpec & MapSpec & btf.Spec end ``` ELF --- BPF is usually produced by using Clang to compile a subset of C. Clang outputs an ELF file which contains program byte code (aka BPF), but also metadata for maps used by the program. The metadata follows the conventions set by libbpf shipped with the kernel. Certain ELF sections have special meaning and contain structures defined by libbpf. Newer versions of clang emit additional metadata in BPF Type Format. The library aims to be compatible with libbpf so that moving from a C toolchain to a Go one creates little friction. To that end, the ELF reader is tested against the Linux selftests and avoids introducing custom behaviour if possible. The output of the ELF reader is a `CollectionSpec` which encodes all of the information contained in the ELF in a form that is easy to work with in Go. The returned `CollectionSpec` should be deterministic: reading the same ELF file on different systems must produce the same output. As a corollary, any changes that depend on the runtime environment like the current kernel version must happen when creating [Objects](#objects). Specifications --- `CollectionSpec` is a very simple container for `ProgramSpec`, `MapSpec` and `btf.Spec`. Avoid adding functionality to it if possible. `ProgramSpec` and `MapSpec` are blueprints for in-kernel objects and contain everything necessary to execute the relevant `bpf(2)` syscalls. They refer to `btf.Spec` for type information such as `Map` key and value types. The {{ godoc("asm") }} package provides an assembler that can be used to generate `ProgramSpec` on the fly. Objects --- `Program` and `Map` are the result of loading specifications into the kernel. Features that depend on knowledge of the current system (e.g kernel version) are implemented at this point. Sometimes loading a spec will fail because the kernel is too old, or a feature is not enabled. There are multiple ways the library deals with that: * Fallback: older kernels don't allow naming programs and maps. The library automatically detects support for names, and omits them during load if necessary. This works since name is primarily a debug aid. * Sentinel error: sometimes it's possible to detect that a feature isn't available. In that case the library will return an error wrapping `ErrNotSupported`. This is also useful to skip tests that can't run on the current kernel. Once program and map objects are loaded they expose the kernel's low-level API, e.g. `NextKey`. Often this API is awkward to use in Go, so there are safer wrappers on top of the low-level API, like `MapIterator`. The low-level API is useful when our higher-level API doesn't support a particular use case. Links --- Programs can be attached to many different points in the kernel and newer BPF hooks tend to use bpf_link to do so. Older hooks unfortunately use a combination of syscalls, netlink messages, etc. Adding support for a new link type should not pull in large dependencies like netlink, so XDP programs or tracepoints are out of scope. Each bpf_link_type has one corresponding Go type, e.g. `link.tracing` corresponds to BPF_LINK_TRACING. In general, these types should be unexported as long as they don't export methods outside of the Link interface. Each Go type may have multiple exported constructors. For example `AttachTracing` and `AttachLSM` create a tracing link, but are distinct functions since they may require different arguments. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/contributing/index.md000066400000000000000000000067571520243672000252220ustar00rootroot00000000000000# How to contribute Development happens on [GitHub](https://github.com/cilium/ebpf) and contributions in all forms are welcome. Please take a look at [the architecture](architecture.md) to get a better understanding of the high-level goals. ## Developer Certificate of Origin The Cilium project requires that all contributions to project repositories carry the [Developer Certificate of Origin][DCO]. This is as simple as appending a footer to your commits: ``` Signed-off-by: Your Name ``` Signing off your contributions this way means that you've read and understood the contents of the DCO. ## Running the tests Many of the tests require privileges to set resource limits and load eBPF code. The easiest way to obtain these is to run the tests with `sudo`. Run all tests with the following command: ```shell-session go test -exec sudo ./... ``` To test the current package with a different kernel version you can use [vimto]. Once you have installed `vimto` and its dependencies you can run all tests on a different kernel: ```shell-session vimto -- go test ./... ``` Use one of the [precompiled kernels](https://github.com/cilium/ci-kernels/pkgs/container/ci-kernels/versions) like so: ```shell-session vimto -kernel :mainline -- go test ./... ``` ## Regenerating testdata and source code The library includes some binary artifacts which are used for tests and some generated source code. Run `make` in the root of the repository to start this process. ```shell-session make ``` This requires Docker, as it relies on a standardized build environment to keep the build output stable. It is possible to regenerate data using Podman by overriding the `CONTAINER_*` variables: ```shell-session make CONTAINER_ENGINE=podman CONTAINER_RUN_ARGS= ``` ## Project Roles If you'd like to contribute to the library more regularly, one of the [maintainers][ebpf-lib-maintainers] can add you to the appropriate team or mark you as a code owner. Please create an issue in the repository. * [ebpf-go-contributors] * Have ["Triage"][permissions] role * May be asked to review certain parts of code * May be asked to help with certain issues * [ebpf-go-reviewers] and [ebpf-go-windows-reviewers] * Have ["Write"][permissions] role * CODEOWNER of a part of the code base * In-depth review of code, escalates to maintainers if necessary * For bugfixes: review within 1-2 days * Otherwise: review within a work week * When lacking time: escalate to maintainers, but don’t ignore * [ebpf-lib-maintainers] * Have ["Admin"][permissions] role * Manage releases * Triage incoming issues and discussions and pull in CODEOWNERS if needed * Maintain CI & project permissions * Maintain roadmap and encourage contributions towards it * Merge approved PRs [vimto]: https://github.com/lmb/vimto [permissions]: https://docs.github.com/en/organizations/managing-user-access-to-your-organizations-repositories/repository-roles-for-an-organization#permissions-for-each-role [ebpf-go-contributors]: https://github.com/cilium/community/blob/main/ladder/teams/ebpf-go-contributors.yaml [ebpf-go-reviewers]: https://github.com/cilium/community/blob/main/ladder/teams/ebpf-go-reviewers.yaml [ebpf-go-windows-reviewers]: https://github.com/cilium/community/blob/main/ladder/teams/ebpf-go-windows-reviewers.yaml [ebpf-lib-maintainers]: https://github.com/cilium/community/blob/main/roles/Maintainers.md#ebpf-lib-maintainers-maintainers-of-ciliumebpf [DCO]: https://developercertificate.org/ golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/contributing/new-example.md000066400000000000000000000020761520243672000263230ustar00rootroot00000000000000# Adding a new example The library includes some examples to make getting started easier. The aim of the examples is to __show how the library works, not how to implement a specific thing in eBPF__. This is because the scope of eBPF is simply too large for us to cover. Please consider the following before proposing a new example: 1. What feature __of the library__ does it showcase? 2. Is there already an existing example for that feature? If yes, could it be extended without making it harder to understand? 3. How complicated is the eBPF code required to make it work? How could the amount of eBPF be minimised? Please contact the maintainers on Slack if you are in doubt about any of these points. ## What makes a good example? * It should be concise. The less code the better. * It should show a single thing. The less configurable the better. * It should be well documented. Even a novice user must be able to follow along. * It should produce meaningful output or have an easily testable effect. * It should have as few requirements on software / hardware as possible. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/contributing/new-feature.md000066400000000000000000000027671520243672000263320ustar00rootroot00000000000000# Adding a new feature We're very much looking for contributions which flesh out the functionality of the library. 1. Have a look at the [architecture](architecture.md) of the library if you haven't already. 2. [Join](https://ebpf.io/slack) the [#ebpf-go-dev](https://cilium.slack.com/messages/ebpf-go-dev) channel to discuss your requirements and how the feature can be implemented. Alternatively open a new Discussion if you prefer to not use Slack. The most important part is figuring out how much new exported API is necessary. **The less new API is required the easier it will be to land the feature.** Also see [API stability](#api-stability). 3. (*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. 4. Create a PR that is ready to merge. This must pass CI and have tests. ## API stability There is an emphasis on compatibility even though the library doesn't guarantee the stability of its API at the moment. 1. If possible, avoid breakage by introducing new API and deprecating the old one at the same time. If an API was deprecated in v0.x it can be removed in v0.x+1. This is especially important if there is no straighforward way to convert from the old to the new API. 2. Breaking API in a way that causes compilation failures is acceptable but must have good reasons. 3. Changing the semantics of the API without causing compilation failures is heavily discouraged. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/contributing/windows.md000066400000000000000000000205541520243672000255740ustar00rootroot00000000000000# Working on the Windows port The library has basic support for interacting with eBPF for Windows (efW). Things are subject to change because eBPF for Windows has not had a stable (signed) release yet. ## Differences between Linux and eBPF for Windows * eBPF for Windows has three distinct modes of operation: an interpreter, a JIT and a way to compile eBPF to a native Windows driver. The native driver can be signed using the usual mechanisms. It is likely that a stable release of eBPF for Windows will only support native drivers. The library supports both mechanisms, and relies on the JIT for its testsuite. This is because the native Windows driver mechanism still comes with significant downsides. * eBPF for Windows has a large user-space component which ebpf-go calls into via dynamic runtime linking. This uses the same infrastructure as CGo but does not require a C toolchain and is therefore trivial to distribute. ## Exported API The library only supports a subset of the full API on Windows, because the eBPF for Windows runtime doesn't yet or never will support certain features. API which are not supported will return `ErrNotSupported`. Some interfaces such as Linux-specific link types are removed outright, but this is kept to a minimum since it is very cumbersome for users to deal with API that change based on platform. ## Development setup The port is developed using a Windows VM running on a Linux host. There is a [script](https://github.com/cilium/ebpf/tree/main/scripts/windows) which automates the Windows installation. After the installation finishes you should be able to SSH to the VM and [follow the instructions to clone and build eBPF for Windows][efw-clone]. __Execute `Import-VsEnv` (installed by the setup script) to add `msbuild` to PATH.__ ``` PS C:\Users\lmbauer> Import-VsEnv ********************************************************************** ** Visual Studio 2022 Developer PowerShell v17.10.4 ** Copyright (c) 2022 Microsoft Corporation ********************************************************************** PS C:\Users\lmbauer> msbuild MSBuild version 17.10.4+10fbfbf2e for .NET Framework MSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file. ``` ### Compiling the runtime !!! note "Pre-built eBPF for Windows binaries" You may be able to download precompiled binaries from the [efW CI/CD] pipeline. Look for an artifact called "Build-x64-Debug", which should contain `setup-ebpf.ps1` mentioned below. The upstream instructions currently explain how to compile the full project, which takes quite a long time. It is possible to build only some parts from the command line: * Installer: `msbuild /m /p:Configuration=Debug /p:Platform=x64 ebpf-for-windows.sln -t:"installer\ebpf-for-windows"` * Unit tests: `msbuild /m /p:Configuration=Debug /p:Platform=x64 ebpf-for-windows.sln -t:"tests\unit_tests"` * Clean: `msbuild /m /p:Configuration=Debug /p:Platform=x64 ebpf-for-windows.sln -t:"Clean"` After compilation of the installer finishes you can install the runtime: ``` .\x64\Debug\setup-ebpf.ps1 ``` _(You can pass `-Uninstall` to the script to remove a previous installation.)_ You can now run the Go unit tests of the library: ``` go test ./internal/sys ``` !!! note "Tests fail with `load ebpfapi.dll: not found`" This usually means that either the Windows runtime is not installed or that the efW installation folder is not on the PATH yet. The latter tends to happen when executing tests via ssh, since sshd doesn't pick up changes in the environment without restarting. Restart the service by issuing `Restart-Service sshd` from a powershell prompt and then re-establish the ssh session. ### efW extensions efW separates the runtime from the implementation of the various hooks / program types. The hooks are shipped as extensions in a separate Windows kernel service. Installing an extension involves two steps: 1. Installing the extension as a Windows kernel service. 2. Registering the program type(s) in the "eBPF Store". For [ntosebpfext] the setup process looks as follows, assuming the extension has already been built: ``` PS C:\Users\lorenz\ntosebpfext> .\tests\process_monitor.Tests\Setup-ProcessMonitorTests.ps1 -ArtifactsRoot .\x64\Debug\ Creating and starting the ntosebpfext service from C:\Users\lorenz\ntosebpfext\x64\Debug\\ntosebpfext.sys. PS C:\Users\lorenz\ntosebpfext> .\x64\Debug\ntos_ebpf_ext_export_program_info.exe Exporting program information. Exporting section information. ``` ## Debugging Debugging on Windows is a bit painful, since we call from Go into `ebpfapi.dll` which is implemented in C++. There is currently no debugger which understands both C++ and Go. The most fruitful approach is to use [WinDbg]. It will catch exceptions in C++ code, give useful backtraces and allows stepping through source code. Run the WinDbg GUI as an administrator and then open the executable via `Ctrl-E`. At the prompt you can set a breakpoint on `bpf()`: ``` bu ebpfapi!bpf g ``` This will halt execution once the library calls into `bpf()` inside `ebpfapi.dll`. Use the [`CDB` commands][cdb-commands] or the GUI to navigate. It may be possible to use [CDB] to debug via the command line, but this doesn't seem to work via ssh. ### Windows trace log The `testmain` package has a small bit of instrumentation which enables tracing of the efW subsystem on demand. Simply pass the `-trace-log` flag when running tests: ``` PS C:\Users\lorenz\ebpf> go test -run '^TestMap$' -v -trace-log === RUN TestMap map_test.go:54: WindowsArray#3 --- PASS: TestMap (0.02s) PASS 100% [>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] base ebpf_api_initiate returned success entry-exit ebpf_map_create entry-exit _create_map entry-exit _ebpf_core_protocol_create_map entry-exit ebpf_core_create_map entry-exit ebpf_map_create base eBPF object initialized object=0xFFFF8982A875BF30 object_type= 1 base ebpf_map_create returned success entry-exit ebpf_handle_create core ebpf_handle_create: returning handle value=376 base ebpf_handle_create returned success base ebpf_core_create_map returned success ... ``` Enabling the instrumentation can fail if the tests crashed too often. In that case you can manually stop and remove the tracing entries via the GUI: `compmgmt.msc` -> "Performance" -> "Data Collector Sets" -> "Event Trace Sessions". Look for sessions containing "ebpf-go". Rebooting might also help. ### Interpreting error codes efW uses several layers of error codes. * Windows [system error codes] and [RPC errors] are sometimes exposed by exceptions, which appear in the trace log. * [`ebpf_result_t`][ebpf_result_t]: wraps Windows errors and is returned from "native" efW API. * Unix-style errno, as defined by Windows' [`errno.h`][errno.h]: wraps `ebpf_result_t` and is returned from libbpf and `bpf()` API. Unfortunately not all [errno values] line up with Linux. This usually manifests in cryptic `Errno(119)` errors. [efw-clone]: https://github.com/microsoft/ebpf-for-windows/blob/main/docs/GettingStarted.md#how-to-clone-and-build-the-project-using-visual-studio [CDB]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-cdb-and-ntsd [cdb-commands]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/commands [WinDbg]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/ [ebpf_result_t]: https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_result.h [system error codes]: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- [RPC errors]: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--1700-3999- [errno.h]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170 [errno values]: https://github.com/microsoft/ebpf-for-windows/issues/3729#issuecomment-2289025455 [ntosebpfext]: https://github.com/microsoft/ntosebpfext [access the debug version of the msvc runtime]: https://github.com/microsoft/ebpf-for-windows/issues/3872 [msvc debug DLLs]: https://github.com/microsoft/ebpf-for-windows/blob/7005b7ff47e7281843d6b414cd69fc5a979507c8/scripts/setup-ebpf.ps1#L17-L27 [efW CI/CD]: https://github.com/microsoft/ebpf-for-windows/actions/workflows/cicd.yml?query=branch%3Amain+is%3Acompleted golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/ebpf-go.png000066400000000000000000001151661520243672000231020ustar00rootroot00000000000000PNG  IHDR,#+< pHYsP7dtEXtSoftwarewww.inkscape.org< IDATxw\UǟN.pP+͑ܙff,-5eff{oMsV\doy~88ǃrg3ޟ$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$wU!!(!ja~R G I*@6 xb]FHHզdʆNl՚3d&I/W.ed|8Uē+d;k x2CBB޾CmKϥw}m:|Ffdש-ɲf@8ЮJB@a_oP#63.qT$_h% l*Y)KHH<Ռ u6V7/vyWOl.Q݈n>mD=L~'xlIxrDw'o#&Cqd7bĖO/Ŋ-VliK~빪_ F*7$ObP)u,3O!O3|S!Q{ڈGiU4"ΰt'D%J f@LOנThشb3Jo}9T-UI֭/Ǫ6Q@[NOrJ^ú+$X;<]w7ջw'-D"e2|+nFGyqU=P_nPgr(*Z-L&Ve2Y^c0>G^oFbdTiiiRRR*~$p!ԃD2E[%uGاc;A#d!GT dEWٿ7W]\\:ժUkdZZZVkۨQ#e@@ߟ_j: T JsrrtRٳz?v"m7cr ŔfV   oW= +7VOŹKSd:= .(O6fe=T3{gY`]U?6lp|vvv@VZ5kF P*VѨKpy^~UuFZZZ5ٛ17MFxmJ.0nPcO5'*~h(/jر/z)lllJ\9''˗/s5%3# L&3vv8;9qqBC1p@Z-W\aʕH`״iӹ(T͚y-[Ʈ]hҤ O=ZBu6 dٛŋPߘaĈLݺ=U\~#F}uV`t ^<2LH2 \ՉTVWF.0V'N+Iju7__߿Ǐ4```ʕ_x{{3p@:vXdD.b|Dի|嗄5n]4xG*rロq111[RؒL&RU{#+o7H%2-K6'qٰj<;$X6ꐐ jc޼yܹ^z1|p +{7ֺ/|dmСhժj3g0{,-cѢE1e܋2,>!!qdzzTq|E[CZSilU%`=d@jodeY?HRVZ9}ܸqe֬Y:uqѽ{wHS(U%;6tԅ?a8x̌ k3 hd¿~nG*ҶTztʥSS/aJx+K\&l=…0ǠopSټ-#!%1a|JJ*~ IA\\\.3GZjϩSxҥUd6US˂?bxt~[l>Mօ\C!7&Y5{;u):_ښM_x06xYYĢ7#Fhh蔆 j|be09s&#G}YjPVN_~W %, F'?q\z2 NV{_N<󜧗T.^C}YdPA Ru̹Fy8X}AͤooGAhDի… &޼y3O?4 fvZNdrY. O0jH쵥O_}vً={ "Sgo/&| 5g /| Lṑ?~v8}4~h]v?,H 䁁[۶na„ ݣ7n0aprr*)΅~z=;ח6mZҋkn&NP_~nw ~t^<*BѩeN#sqvޮZxipu 6;X0WGe̘1<E 0n8&L_|QfedͲs! Oql?j4<3p015{m[^YբMjA1ECf4 O4ahXf!uLt%! ÍPnC>^Sׯ3h Yz5aaaҙ53炅ynt7 ;oꫯлW_.P`?Z=WWn}8ulٱM̼@t.^cqN\9+XO_\m% ^Ѷ>HS‡=zoƍiذa%ˬXb%]v)Uqt~3|,YxkmLJJ:NSg o¶fWNy 56 N:8g-FUdXF1g$zHQƤ6m 0a[+.]?cFU(OaۯXr Xl Ç/>e'uвx">oi*yN1Ɇ*4l'FFMk~2O}=G֯=qR^ns3!*5wF>xzz>W_}%\gy___ϟ_b%;NBԕ+^^^/&Ov_n]//eeCv\rdD/1>|7g8wG/-7k;Ve%nIIL|;<_{[;\Q's&=@kWOV] `=\/>|gOԬYڥV``ZZ`Ϊ N~>4hp=NG]@'@  7Eqȍ@ Z6=2HQv/RaÆ K.eɒ%/%:\Zr6Ub޼1Rg+h>;Ij~=Q#צ+ %Ni !յ:uz 6LXhk׮eѢE*Vr+׮gkJ/Gj۶CëȼĪ$z8prqq?m4EذakRBaG8:8Tb{Q4jԐ> c_D=ӝq_|JT:uY9q۶m4e(-8g.;#$$@Μ9˩SiѢyuZ͡CD +FX8r6n?\)bn޼ݻU5gٰa#zNN*1Lj$z,:tbƌ%JdZ^Xy4MYT: @zzzuA1wߪ< +v 5HnCwP;] \.QUZUM\4e2uIT} 5k2`@++Wb̚}o@ҧꑼ=]-}HU@mV5k|l6Z,G;;;e@@fJOO/WWW\]]j ׯ*iӦZ J9*+C<ٹ+Vгeeࡼ9{,} ա,( YnGifgڵUM6U !!!h%۔ꫯprrd.V`ULL NNNX|׌>[6PVpHTTTd|pxWTj߭[7Uquu-f$$$зo_BCCu1'/Xu΢%e9t9 }vh$Xeýn:?3fM^ey„ؽ?oߎ])*\J#F'S #BlS'Yl=bZMfoxJBBB&5o|3om6X<ޞ9~ cw'11mzFA&M5j={a0ڷo?[΃(KFjUYE u8_~)fA̞>߿`V@zq-.0K֮ZZ6?Nƍi۶-L0K5 ?~7oX@}-WմkߎK.w1mިB$@,+puu5fۊ8cXLo{nWW VgUd;nnnDG tƍXzK/ٳշv:I/[]Aqc;N7ñ !aYӭ[*,dddX^#(%/FN"c_|"/00 |M|}}9u .`[hQy2dnnnȑ#G0 nӪi]Fcr2/]bEz])j߾}1;Ə{Mƍu?¼/@$XV`2\*m{{{bSdfeMl~<]_ҽG|L˗III>sLHHF@z?/bcc3%׬(QlTt03#CTT4 ؟{1|HJ%4+*Cqryf ˖-cQCMk?dʉkEͬ,ݖM!UeիW߷}֖4ov s`.G)ZqL-͚KvZV\ɻKdd$+W&<<%KGn:3yw;Ŷ:Y6-.aVV66lΎ=u6Aff4ӨJͰ7h &~>'N(>;6]>l$SLVZ@tz}-[gI^8Qi ñ ϣ }5߆CI6G/,j/ճð86vaիd̙?~  syIMK+'NA%*{?t$/]fK8~2AFff+V.AM^~rlݲϏuZ*;quuԩSyzzubܟ~t=ugს}7.];hcB bccW/B!GVZut|Tq5j9-EpE9cyKsY`ѣ?| ...nݚ]vuV9̙36bm5dR}a clj֬&w˄&99c^k={,_OOONDiݺժUږ#诿KgoO坜4٪+Ν;wΨÇpvss < 1)(罽=g?O"1`W^kcuYc5*ߖt_|RMC0d ~~~KIIaɈé[nuΜ9s3'ncIh߮#X,>39x:>u\]]xZiӦ#/E˖O0i C~8 gJe֭q(|?2YiV^a z֭[/~V7n$%%M6e1gΏ4iҘgϡR8s GcNywNKB`xyye9{,Z6_֝ک[7DxѶ}JLH`"R'4%,9vJRhѢ;jϞ56J4j=KBغ2tɎ%Kb;&&3{#7בW4Μ9KGb1u/4#ޟ0.]1`f͜M.OаaC<=}zW||<9nFӞ𻇇qc=BHS’%,,ltxx[lqyWg߰Υz{KL&3 L~s 1/mȜ; IDATjfq ;"dht,z&$m ,3g}uoڵ밵K.&Ow7745k ""l7oVܹޓ Tb*ш+ѣx~?c$X%DR綾^M>Ӄl斯dh0U֖;?ҍE77RliѲ5wExyo־]GvY, ǎgܶsbY8ك&sp:2Jd?\_c$X%fu~СC^VΏ>ʕ+_~w& F sq}`Ÿ7+ZYx P8sl{9p^dhު-hIYɡolڼRglݰѸLFLY+徹N_e٦xxl]2߿ H)a$*!DFFƤ!->EF&9_Rϲp =!>f試SEVި*8cf5#m Kͱw`Mbܿ{rL4TS9_֥GK3ӷO۷v:u4>>5P VIqpvvޜeA77l|:y$ρ (6Ռdl`6[U֯y?|VմĮbhܝ  g9wCp<2'G{kQ+4uWOHKK ;38ujWӪ'**v5#B7wM^3l og0{O+4 ܹsV͛^zRfh슏wd4c2kcU._ SPZRqu(QMG!z87n5gTDD%0r,(psu/脽Z4lmptt@`61s2#!>܌Fb|,112pXS] @j q֖nhuNo3Mԯ_Tmly'n;w> N!$*ڷoeŞ3fsܹUd4c6[Mt:sĖ+fbX;%+Q1cT;ڣ(A>ɿ&ѹ-0!LL7D3dd!#[ C Y9MA'b#bg62x:v$IM1.eܴx4Q9{1Je` &fv V P( :e޼yE\~}ҍ޷GRݴ _fsl`}6sNi-Uи9"3w8>Boe,fHe=ۏjoݫBɡ~Fbbb*G)L P*:z٤M}-)+cАa [wjwT1W^g՚\.uDUNob)WF9o#o?^y@ Sh+ Z,sPi&qwzJdd[~4e\1tܩAR,c$X%/((XA7o|8[mz5 ܽҡZ%|bZvxƍeV~=WD,f3K:5;t}NX,>̲~r@Af2Z7Ѡ>Oϲ`pO1Tw:IAPd<]VnzB^A)a h4N>mo|8ivH 'Z.YYog[AX,"fK5belZ.]Q]#7PҤ wFTm^b%?cWդgY8g'܈7hU)=zWKe9u ƍUbC$X%@vc=? 94Kv2d`ƍyC[b3yߗvɮ9TӪ h^ TciRW  r,2Lû:cA4lؠMcAI۶nm2U-:'hzz_wMy S\ N_KiӢ9"z[DnQDShlBD];Obb<@z5*^ [ԒħO1s3$1"Qo?BDȷ|[|HN{fW!`q_RRG|1pG"&mIV!]࠵G%!E͛9| N6:vЍ $sOwtt'n׮݋:cUpKWjBa(fm]^To B'-81A H߹ rAQD*-{ i]3Y$'`'_;gϧ?Yz_ H?P+G# ]_0`@d3[m=k"@lwPyA +:pOP0L.ukj`ܨm6/ădbɒo6~ˏ;FVN \j&O{("DO\ݲQ]6c"WWŒJ5yf0^J3hҲ-'Os%JAo5}"u嫗,ŋ-V.\t E QƴȹE2L}^m2O?¶-jU_Qą=9~-9 yfGiGtt4?){͚Zjm1c򠠠6kcǎd3Z$*Zj}t̙y%)<9x 6͟T2AMUCÉ#Fst"6o-D {os^1 lݺM_ӣ[j7|_vʱGy"$$SN-(iH̟}3%=GJ&͸{%\e nn4ST0gӻ{7Cp2)q={Y`a֡C9nȑ%Eiff``O:uwV}ЧOnF>Lp,X͘x{{Ѹqc7 #,`|0 DG͍7 \rQVuʠACQǏϛ7i~)o{"""|Y$FX,r߿?{.3DJA.WDKxYSzY*޽;ݻwϻ+b_HHH@VHPpիWϏjiejj*YYYdgeDrr2I$%'HҭkIIɀBZGX 4BmnԨPnݚf믗[xevv /6$*sy ѵ55AjEH*>i-mBZ __߼Q:E^xsu=AT bc0Z-NN89&&UTn8defb4dee67ƒ}ш Ȑ刀-2o-uÃ:uqm튫k[g0KrJ`ذaǏd2P(%'ԩS뉢_y! VEQ, Y =<1U+F~B;qWg? ,j5Z`0EHNæykYTֳ(JpQklQUdr4ڢX=0gYVKF~/hsǎu:ݏ$XSSŒtX$"^>daRݷfgcSEֱ htz9xxx`h駟,Xz^|r-`w-{b{2E;Zʘ"`, "D}>*G^ qm2Dse2+.s{Q СCܸq#ORSS˜lĉa($]FJ$*h,W^SJ }`ٱzyRU...tiۆۋ,gɘ1{kf?47oNjHKK (V^]bŊ6:DǼ$# Vd r$Gz(Z;ҮeȍMݻ9~8_|];ke4iSzzzM[Kۢ0M33WvvvxO"Wҿr )fI L6-ޭwd2 k8VZa2b2# V(9F}>7B "E([V\ѣG_>ZBY.l=s@0YDT%-Udg`9Rex?z~iiK"M KXCTRmd'Xݻyٴi۷筷ޢyԯ_GGG,d Ɉ<[܋ށ Xrh[1N8Ql6/m}\$*(Zz}0Gиoүdŕ{$g2c  0`{j8}tqvvƔQ}k\1ՐҾӐ&L@L45klmmK&55U@BC7n ].\vsg r+՘(r7o[ryailmm7oӧO/S_Z;-Ƭ,v|$=899%m&jmڴYf$&&Ju.** {{+Y̙3A/M]{FX%@WUbRjh\]M):h2. |Y+11s$o1L8pHJ7ى*Cϥ˗/2j__{ ȑ#Y5j(՜xuzԕIJF… @J.Dro,)_VlllkfL&222ٻw/7o,u_Z{{L9Y^Sĉ޽{VgϞc64%,7n(`efe'XX`V0 nMkJX/--/B7U,5k4t P!°BlFe !&lRR%j[W6HiUfs\LLc% ǨqP;,2Yޡre/bT* (S&A^K;ښAD9S&bY eTwQǴPRѩg;u7%--Y:EDv+­Q滛WFF;v߽{w^_v=HU2K+<<ɹ^wPeg}He8b|}},ͨQJmSSQ:} =2g7"##|.^8^z6hDDDlv IJFBJJJO_pݽ*ŔN&`*]YL:ƍxQFL:\[`Y}Q`g6X\]=N>-;v+Rx Ձr31GZt/ ^y/ rY/2 [[qpp'fY899駟r;ԨQ#j׮]vFEӚFrTai*}Az7̚5+孷7ofs`,M}< *$NWjJΩuuwҥK֤"Ujժ]m^}Z*`!ܵ Nl\M̀||=hͻNIIamڴv%xϯ!֗ϣ Xq;uLa2LddeW_5N;W`A*:pgA#OSHpϕA=za Slޱzhn||<}QZfͦ6 kR-Ḳ(̅Ql'CR6qzTbWT"bmhUCZkV+̸N.̛(5xnʵ=XE;v&O߽{)cƌ)stix E?B0DQL=}]z,+ N ;IU ""  kX#6w\qwlȲ+"^t*Ja,o)&wUб 'gΜZS/""ŵtZLv\t[!Pd9w <[h!sQ#sȻG0!5PD%.=x4kINHqW.r(9R7$/;Ȟ={^k֬Yb>-/^DVDݡeL "O~ٰõwׯ\ U öU8J`u5VIѱh|5wkEGG 2\4gպh~~}>պ.h&-8}jUcN9_ l@'2ScFR.9,`qOV(4+_x#%Vva4WTq1ev׳͜z]Y֎Ͼ;ypםs=]L̪gU=I7Anh2}mlLRLbcWjuFq=6P.4fJLLL?k%US{SYIRy IDAT9˗boEpIbFԮ[hf]6.}{1^D&L&튓MAu@o;H] z*Q./u9g2Vt?u2ז2i&Vo>^g-\/ދOt\n}T{)+`<~ْ/9t͚d[X>bzRRR~߄bH5]Ӡ=)'Gqq7˃ Zy鍢 ޚ)%?2rx3k KJMRpƍxEc6ܜ]ݐ &2RW+C^yE ]!RT#³Lq(,VA vttۭ3VmeR_2WwZ{Gq*h`=+V7333kS`Jxu;r.͚fFˡb0o{jm1[#NOdQLV(N{9weNLɩR6it:PaS<,gF4-& :h5{6"Q>GvN|1ۻ陥J?4pУ[61#"үqupӱ'O;[/}8F"|?! o=gN9,[A6ZZvY€u$",H\%IZ;7nr^*Z9XjOYXmW_OV/u;NگZvskz57߂$-Łͪ$ 1LtvE\8j; Ɗ+Bw}7>s5۝Hx"lBf3 {8m3@S98Cr-Ow-1D*)پnL_Ax_0IL!vDM4oGDXk׮MtiŪ|9Ё\El9hrYརiNH6DsI10=GEEO>\|1ɸn1 O 75Rj'YiZmfffuaaaՠAT+l߾Cp'! y^tJ~5 t9/^[CQ65էC aQnW>@|kE%33'ٹ}+|ci*z=(\.\.ҿD k{ Ykj: _.yѨirHJJZ+uؘPK|`v8|l)uQT/EQ<)Ǡ sYb!gBXWisWsC0 (: Jۄ0~&\w)qq_ vqTWU< lK7TVV󷱺:͒S{.Naϴع;S0ct=h/_}2/fz|ݝ;wf0 .6 BQAa~>@x˨PYUuTII믿FHJJڔdk ֮]4Mˁz&Kny```S9#/mOV+9ߒQa.TxOyd{EX@jnZ\.;d 7& T ,ەQq_+.| _](k`j .mlOLX=$H*G,C^~ f5ӧw~\k~_-k YdUQ HS6rʕfx<ɝV]M! Pmkm BجV.eggYlYYY[HQq:iD7ae7o8람>8r'ŞC{1@yrLl&}w>2*~[lX]ce o-CaNi5 [K9Dcnч1v(X"RZZʷkʕ4x}6 uml{J iV]WN窺-5 MID;)5W^ym<eӦMYy;w,UUyRQQqrAaTط¾GvUk'ʲiԩƍW<ҥK{t=6]+S<:vۧ:~{k/ɄQA~J|"N8ny .8.wr| .ۛ@CĤ&liu y=8HHYi_+̙`ڵi~ȑ\x~Qff{aDYN3:fA =_q]Pb3pkwlc; TWB$ 6'``(i2`/O)PY=|eE% hVkKA]שAׅ|hSfv v<.g8qbĉKicy^o)^~Zrx(+((T"n݆sν5--@ 0 /CG \GcCd'4M~LU:gefFVYs/~sBг؞ACe~6 ϧòM?/IJu7114XCPIV/yM4`0x2~uՕŹy9Ҳeyͷ#[l)OJN~qͦMCr)U$=x)* z:IEI*"IrkC6F,I ݁bsY(vz Z|&l)ݍc(43%H.U:ŰfڬUZ,VZ ץ=֥K6Eїt Kzbd*J9?3~vxM̯jyWʣ<al[_nڴh'MUfe+uq >yGFM1"I텅t]$I7o^N~8hk.83{Nn!U1"(jIi0T͊fYX7jCP5+jۡ7V1%IžARA_Lk'%F}e!uz{l闌:t]3?Y8p`ԩS۸qS|0bРA"޶m˦i~y0tM&@:wJwneI;lZSeZ9346<3yjSٮ{\>;/cBa~̫if#\Nrj\cMdHRQnʨXФܺ_|q;pE,^U駟~řguwFF (uy}c,=o$+]${ __runnn%M6XnMc)օ@n- aPxM CAfxsvvv_3RV ^/9dvbalj*/;ȿS- z x<|ݕ I}0qĪU{ץ?@=9f̘g{w P^^&%uk 1ft{TZWs~8]@W0w^r|S)z4ʭ޾\J/f#w/@Uy $Ձĥ$G_̤{# ?c6HO|i驗̛oVYYqsѣc By; ޓ9'Bj^ǒb?tUaXIQ~E!2ֵOW .^,XWæ}[/9͛7/'0r =j&=(@Eòhͅ(6)`{%'daek;Y%;(o2;lz|RE '/ʖ-[馛KZ4y½8hVԢNgg-p -up555֛,C}pq z$G%aYƮe%IfwA|y?EUh7L*"|Uy5$ܙkׯeӼ#G}̭{nus[ne~4y .8VP>XKw*++o׋=Yy6cGr'3lX]xO05‰wTk)i"ItB4kŦJxu;bͰ.$Si#JKMĠʕP\\_~GhWw|gG}t͛ ǟS駟f?V~޼gG~@0XR[S{ Qžw~M/<BD8ٳsFraҥ,Z5/ LBJVV1eI"ǾӀZ(jIBa% U@&B.Nw/^z0\eW]}}j?w%ÏF7nܨ{~5Р?}YǦ!Jt3f0#;v,K+ᔖՒ%,\%;Jv#spr$:8]ndYnSP} -X@A1:*v\Ʒ_%k2aD͘qby `|ajjjn#ًz +AH0sz1z}999L?H&Mİain:֮_ǚhh򒚟OưdFѸsr:T,JU"L)DDvSaQa`¸1L8#&O&)Us՗/[a;jkk#PCKX BcLˁmи}| c ѦF8|9#Ftk!uزmUUY&-?Ԣ~Yܟ>paRoósu۷P^ +3a1rƍG߾^:˗>wPWWGQnU^/눗HIIoZuZRRr닿܋%!c1=u@ְ󽷑v4`UU ѣ5j,4M***())%%عHNݧ>Xrg!93ll=?0uCSu% 4VV௮|7UNZZ d 2RXXe ˊ+|TʔE\rL m y}O/^HMM}yf~w֭C}mz0އ{ɌD([_UU4`T 2x O)..FӺ#P(@umeWVQ]SCǃa5}pHNbwX8Hh"!l|~"БeaجVd!?/l rr&7/׳n:]d-sWܣү{׹e7SRtm57X0rđk֬DZ-ge(1eJvx>yCzP$!??rrrׯOVF@&~?`]ױc0˅pnP˱h4ʮ]l߾[v:֬]K}]={v!#)>bfaDѣ =W UsFZds?iKV0jԨ䲊[k7ЋA/a%).=j#wdr'LaV6/;\k"H$¢i¾}#7;>}HKO#--t2IJN"%%]ÇD  QUUMee%555SQQΒʨ% ,cMqcڷq7z,.4э(E#4WK5"Aa왰[>d8\f [nQ/[dޛuKX[ۃLO@ك:Cj@m UWrW _lIx ( LUS1 WRacw8P]rTo-m BC!<H` aHaDzjВS22e>H:4 Èb:Q=Bs& kWՏVR1ӥ"h \kxS\\ijߵtM7l{ѳ% %,4}((H-ҕs@FAz$p0 i`p3c:4;0 1m{-.m "ŭǼx7Xmj˵;8(..jyMaayɌZ2.]Z]]`0\/8T IDAT^LC'h/-IRb% ,#Ir,T=WbGf,~pB)41d&BPFfA fغ<zYiUEرc~ ֭c7XS6_K3-[^F{ ^J ՖXNBD H,yIbbb D|PtЛ ,Mo*b r H#'UEU4d%x$w +^͠A۱c993yCerY1a$|^ZGN]_Z pef~T%@KNvwEӰZXmci`zjb/$uOZC%֖Lŧzٝ5w &_ozǜϚ_zR(HcgL߄ulJ$sT Mxe%畇1kh ҋvJ\yy}/o;{D ͉ٹt Dfjv0@#~cP[HwK%'#7okq[[]DgotLu_Jci) 0#Jqy:Lg!39.K?m!R5&x~aPrѲ:b={C走fKןI~5l&-!L؋;竪 XS,$@ Ir\KNu6fٗ0Ty~-~x!U1<7/غ&=%JhȜ?%=ە @QÇGeU'DLCdzckWv1MkML,R82"9*XmIG@4$2VGK Y b@X+Mo75mu>~d=^J2xW簱 ;wAXEۮaWep}ey7 okﯮjR~LÆՈ@#C9ĄXPwKLΏ29_]myJR! "A!$al)Ad[+0e@D2q0fVUMFH ftKNjFTKGhn3fm5"Q`r?k?VVt>#E/a%G-t &˝1Tq3r#&t:ú[ƛd ݎ^oҸ+e%qBQy>/Jc(O(F4 )kJ[rkzɔ1j6R>NP궹\ADn@ H 4ڐ8YqbY庿W_ l(VgcB/a%#G/tc ~Bp%&l| ns2"'sficN.2]vŗ\Fpq͘[4l`lĴ k2ѨBدY# ]ǖcI P#3j{)dj$ H9G”ę߈3+9Ij4KXs=ZGp%ƵqK,zSxIi~ @?uVgO0ai\ɝݲgl{pE%? +4%Vmtϸ2|1<3gI16O * ViCΓJ8 ZZ) s@WFDYsCjFj&!k”UKHfĶE2u8 Dn^{B * +.-4 \CE,CrmeRYq5?vDoV Do_bNғV#Wpt7ˋxڜZD:w}7{l;p\۫yx.ڧL$ *f`q$E BWJq`l6CH@~1X{&YDtHں_i!Рz,HA)jbO⫱RF 0kogڞy<)`\1WcI-Ùuy9ĭI? U\kyOu駟pϽlfԨ2VrD_#OEt]!dࠩA(![ >HbOj m`K⭵Hf ۸h-yQs,4ȜP%%!h"cKа9.Pmfq6vn+J(J@)@R$[oo%KFqJ + KH6eeZC>uƛ>0z] so0׾ȩ $Y&%5kB |q9G假T P^^΅^ /@n^6aW]yl14gkQuSp@ŞDsbwQ  :aw~R'fb6{"L% $E Ð>bj"K7㶷 ]vn]Oƚe:.&u1n/=CVuU.`EqDMMtUKX !!,I"%- 6ȈHM؏FuYr-1I,Hݐ;]wԢ(aPFu>wC@v[jm| c+P[ CF{CqS:C')K]ݷ+j qQdkxJƿE#)?#3LF=2&;i'X:ׂCyٶ +ׇA/a%F8?q%_;=G3Q>Ŝ{l~q)Spꩧnhâi٧1^BxN^R gU_DM :ɐc@H {.Bа!(@$朾r+J?"iBvBp\pB6 ɓ5~人[C{^MiqۜSV=(LīаJq6++TB >Lkjj?xЏVݲ/c|AJKK5 XmT]{Ի?KfA]IA(јY\}Ь:M64{A L!Zuh2[U*Xy U)U$H`sBȄr}SIG^Jd 0 yɞ~>yva{g¡`h]l6ٹ}RgOoJZ_KNT d (kpeqdD\O91)ԠCDFn C8Ԭr׈">-ԠW h*TZ25iYVc;UDԅ1y1wYsyOnٯVb7aJ/_`Z|l0sx)#Ţ-/M䘦|ǔ9MjHbK&SEv]AD%h[f-3Ăꌑs(qI@HA9 JL'7R7(Is.sϜ~+EG/a%f%,X. A4DcEIѩgv yO2'?{aذ[Yףn(2"i@*[$2bV4~PJʈ0RȄf)QՉF螯TC0QR{ߢ fRF pZA* i$h++c(4F~}K>+?dV`臈z%$ gr:᠏H([WH$_w\?pʬ\sU6Cc3F5hr xkԮt:@VRYiPށsE*_ڽ$#>Ġ~QdhsLF2MZ1 |{!dzrw(4IHsŏ ?!7`áa <I9HNC;WR*V3IL,ls~Ν;ylhop||Nbͷr${KAFS#(ReLLU3 Xz[B 74Ge^q2g@T9FV$!*͋ZTkrl0=(=^ +F(񿌉rvG2È"2>qsŗp9gso馛8q<3hթvcmZK(K3 qj,ՠrA[ojYc+aȱ4Y`%L=v~{s =7^(m"h/uy>ɂ<>íߺh` 8zqLtkjIL|?EHJIoWh6x(xK˙}|{cҤI(=wƝGřŭ+-&nާZp/& _R D׊pq)\cJ'd"kViaL 625#`I7I; ,Y`˗br0Jل DC$ U]:skjY1=-)LaDab+$~走#0ކ@3$ 3b#`:[/ĝwqɹrO#%t'_i:8p$k(jYDm--IH_c]+3 EBdjt &RD2Dl%PV l2 IcJO"C"]F \LGHJtqQ A͘)]{OEڇ}]#-/ΊMaTgHnAkgƴze% `C- 牄iԮ'j>O~'5<8lH"$ sf>o\1?nl/we&x>Q4AR&XgpIvݿE&\!2N&CFREͩ,!t0byYQ9vWhRayء6vmnl-n,CO=$%LC1Jr,eu32py 8sz{W%JjoOgrc'hKXXZm6})+HΛLR’ꊤ:'tv:f,Y럗_3PU&CW߯ntss iȨEKVݢaQcMzs^/O;yheK[5eKIQtΠA>DAyIr*ʤd&7> iN:HIDʺDڼ`Hq]O|{?;~Ի89(">/i8bIyY2es}ٞL`GMfh5:TW3Iy"m>S'e*G]-EKk7c\jiSOfVjѣlzE=^9z +A5)Y\I=2$ˌ26ܫaΟFQ 9y.QyP"Hv}Ow+<( ~4Iqu fI;Ik_ pX~kQ7UB/Yi Fvz=*]z~>nG$g%AVP}}QO@c(S !v%_/pD_`<ш BX.,ﶳ[r^ KdI* }p͚:U~ -~'n0Qap{<.Eb(23Y~jvVB@>\^'V ]Ʌ}{lȵO߇ j핷1,3xrW]i W┰9#ά+$鿿]ƕ>p5Gg{͇LlU{".)0W\_C;ܱ}ƭ!wKQ`>^^'z +A5zj{løvaa,ٴ35O((>[9qz( ݓUr~eIʎ1@.K/^Jv2k9ΪG$P 2{z'(q=/fonU˄BK<}'~F,~3[ȯOM> nu ?4 aIDATn3[wEȒ+24[3UO?je zKmzXC0XƲjwt/#>/ʧ _ox)dՌb6:6?`OM)!0!H,Leq(Xc֥(ymq8!hsm%rx8fpЩ#lXV,ohk6fϑ T5Y#@( 'J2کnȊ<%Ț]g$i߾&,gM t H)4zv}qzg;ssF`M! GNZ܎kz+GT $ @)(dWт[|YI Nq (*)=iRL~yz[c`pr6# IXd6gvЩ @wҀse3б" +Ĭc]4Tt32`)`@2& L  \d\;.z&lٍ؅Qo})()F*޿M!d[:$4 IYN8*IЀքխ{xlGw.zi8X>d8">qsGPQ )(ov3N \dqm_~Lk |j'6/qSNvJ0K2 ܟA(|I=USzUl;őE@iG_Ց7;e'4ՠs08=3ѝۛÇҐIzu.K}.) |z:^۸Uu"`0OD![?ăþb/穸❶WHU|^hz3Mˁ,چs']?Poݞ܎se@س׫5mh3$ V[v;cX|38kv&Vg +{}XpFsP5l-ZuUJ TY}!μuJycS so4h 6W-f6IYdO'<67ʸbPP@2Qku5HMIAذn.:zcNl>]:s}C\g`Fs!]]s9Wxs焱 DigauaWV`*D` -ވ>% =qY3)P_OO`/x Y! WaH-sȠ4 r.qʙV=n7Ը ҇:|PHsS(2x sD+WC=;}&L@; {Z凿*|S޻%X](I WXa򺭶}!UUV߅XewAQZPTV˞5n|+|-`"hH ޭs )A&67G\YP@蝣H E_]ᖠc~Z>؜u^Rn-yWAV9$aO]u!}Ui[ZO<[v 3a@V_lw ]E{ xdr>EHA$a%³>:( &qnP4D +,4 RL)T./(`)p M=>AR( !^z}. iMgz HCe Vw|I7E)PD~C!{_qĒ׏vǨ[}k [@yD$Btw]E=_b7a pbŞ_w~@dD+8B֗ZJVP6g]ͻ'yX cGvj"#B$PNESUo)tVd$<FGIXSx}KVzv9O*VUei5Օ֧fgr=%5{0?g$j:POkuVWQ`.oq7+жvfHga0H9$V?}xVc۱ nkc!N ] i`q:9L,gv) _|.hy)PPe{jjR?$ۧz$ P5 Gs$$(\u荦}yE'9Z:z^p$,G4I&V^zp<=جN[ ;F;A.^΀ q uFy֒}n߉ѭa8tQTY_5?8# +uM蚵}ٽ;NX.b3g/p5ԎI04 k-l~3X$! )9ccXFSe+-Aj8a{[8"~8fY׏N6f+@48NNE:p`Mdہ?{} ;unˉ<oLW Y7oh^g+-AIJg+馪1]ާ[,r8e "'9$aŀvq'%QB__\;Õ^PB8ZMVl ߭)7_6۳ :Ⱦ`6GK጗{Voq +F0 3ޛ~;N+ameeY @WfɆΌASd hA)UYk=E fEUUՈ-J`xkC_JB sUx}qb3Kv|_hs# +v)E_?\B'>]UUt5ҰD'ȩg<+MbGDIX1$)8k㟾a8ywI6+3rG9N*)) ^e;?n7q\sFEq'-`춚'/uz}mLǂQ|׋W^PgϞQu=$aE)MӨe˖YYYtCCt:鄄ZQ8,R$ PZfMwkWy)4mncZNۂ -e+ $SvgJ˶&79s;v˲(q$I,G/TáFRMJJR'N륝HŠ<iQ$I 9fƴsr{k/?iW;J;yhصnizvMy'nCxf+|,˪ǩGy^iZaFy^)))Qjkk]j;ʐ"k֬N'qi95d֯_oڼͅ?z[vWVŰFp762z|#ώwⳏN?MǷ4ٝ{_yaa6^/*nh4J#F$vbhZx+W33 0L,F%D2YƽMtڏAgzfqfF4MAVMS_!M "k (^W0L(Ӗ/_j*i䂡Hj\/LիYe#+> x$izl-.ogJxg\>)b8qb]Qwuz bTwFQ;_|)q'Z.\wU#Z\)z}Zq8l%A59$lC#GlTNQTTU2eNgzӃm-. Wn؆mƖF}87:NiZT:zh?}Ixrss!CE`Y;1T2k]s-i՗YъPU _M[dmsG8ܘc4! y^/iŊyyyl暛9aXYMӘH&K(QiVsu v$+IiO͙3 REQ4^/˲, IQP"K$04^lߍp7d ovI(J98i$K8\DzJӴBQ:⽦F|d{]-IͲ,msEBNNqݪlVNj4UŢfuӦM*IFс1 77ٳ'e6)BFJSNyx<4#U88n7e2 I W{H:NeYͬ1~m.I&If45I4ɤ|>iFQEQKMMU[ZZ4ŢM0A%.3.sϞ=;sB e_                    BXJ*IENDB`golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/guides/000077500000000000000000000000001520243672000223235ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/guides/getting-started.md000066400000000000000000000273641520243672000257660ustar00rootroot00000000000000# Getting Started with eBPF in Go In this guide, we'll walk you through building a new eBPF-powered Go application from scratch. We'll introduce the toolchain, write a minimal eBPF C example and compile it using bpf2go. Then, we'll put together a Go application that loads the eBPF program into the kernel and periodically displays its output. The application attaches an eBPF program to an XDP hook that counts the number of packets received by a physical interface. Filtering and modifying packets is a major use case for eBPF, so you'll see a lot of its features being geared towards it. However, eBPF's capabilities are ever-growing, and it has been adopted for tracing, systems and application observability, security and much more. ## eBPF C program !!! abstract "Dependencies" To follow along with the example, you'll need: * Linux kernel version 5.7 or later, for bpf_link support * LLVM 11 or later [^1] (`clang` and `llvm-strip`) * libbpf headers [^2] * Linux kernel headers [^3] * Go compiler version supported by {{ proj }}'s Go module [^1]: Use `clang --version` to check which version of LLVM you have installed. Refer to your distribution's package index to finding the right packages to install, as this tends to vary wildly across distributions. Some distributions ship `clang` and `llvm-strip` in separate packages. [^2]: For Debian/Ubuntu, you'll typically need `libbpf-dev`. On Fedora, it's `libbpf-devel`. [^3]: On AMD64 Debian/Ubuntu, install `linux-headers-amd64`. On Fedora, install `kernel-devel`. On Debian, you may also need `ln -sf /usr/include/asm-generic/ /usr/include/asm` since the example expects to find ``. Let's begin by writing our eBPF C program, as its structure will be used as the basis for generating Go boilerplate. Click the :material-plus-circle: annotations in the code snippet for a detailed explanation of the individual components. {{ c_example('getting_started_counter', title='counter.c') }} 1. When putting C files alongside Go files, they need to be excluded by a Go build tag, otherwise `go build` will complain with `C source files not allowed when not using cgo or SWIG`. The Go toolchain can safely ignore our eBPF C files. 2. Include headers containing the C macros used in the example. Identifiers such as `__u64` and `BPF_MAP_TYPE_ARRAY` are shipped by the Linux kernel, with `__uint`, `__type`, `SEC` and BPF helper definitions being provided by libbpf. 3. Declare a BPF map called `pkt_count`, an Array-type Map holding a single u64 value. See `man bpf` or the online [bpf man pages](https://man7.org/linux/man-pages/man2/bpf.2.html) for an overview of all available map types.

    For this example, we went with an array since it's a well-known data structure you're likely familiar with. In BPF, arrays are preallocated and zeroed, making them safe and ready to use without any initialization. 4. The Map definition is placed in the `.maps` ELF section, which is where {{ proj }} expects to find it. 5. In BPF, not all programs are equal. Some act on raw packets, some execute within the context of kernel or user space functions, while others expect to be run against an `__sk_buff`. These differences are encoded in the Program Type. libbpf introduced a set of conventions around which ELF sections correspond to which type. In this example, we've chosen `xdp` since we'll attach this program to the XDP hook later. 6. There's only one possible element in `pkt_count` since we've specified a `max_entries` value of 1. We'll always access the 0th element of the array. 7. Here, we're asking the BPF runtime for a pointer to the 0th element of the `pkt_count` Map.

    `bpf_map_lookup_elem` is a BPF helper declared in `docs.h`. Helpers are small pieces of logic provided by the kernel that enable a BPF program to interact with its context or other parts of the kernel. Discover all BPF helpers supported by your kernel using `man bpf-helpers` or the online [bpf-helpers man pages](https://man7.org/linux/man-pages/man7/bpf-helpers.7.html). 8. All Map lookups can fail. If there's no element for the requested `key` in the Map, `count` will hold a null pointer. The BPF verifier is very strict about checking access to potential null pointers, so any further access to `count` needs to be gated by a null check. 9. Atomically increase the value pointed to by `count` by 1. It's important to note that on systems with SMP enabled (most systems nowadays), the same BPF program can be executed concurrently.

    Even though we're loading only one 'copy' of our Program, accompanied by a single `pkt_count` Map, the kernel may need to process incoming packets on multiple receive queues in parallel, leading to multiple instances of the program being executed, and `pkt_count` effectively becoming a piece of shared memory. Use atomics to avoid dirty reads/writes. 10. XDP allows for dropping packets early, way before it's passed to the kernel's networking stack where routing, firewalling (ip/nftables) and things like TCP and sockets are implemented. We issue the `XDP_PASS` verdict to avoid ever interfering with the kernel's network stack. 11. Since some BPF helpers allow calling kernel code licensed under GPLv2, BPF programs using specific helpers need to declare they're (at least partially) licensed under GPL. Dual-licensing is possible, which we've opted for here with `Dual MIT/GPL`, since {{ proj }} is MIT-licensed. Create an empty directory and save this file as `counter.c`. In the next step, we'll set up the necessary bits to compile our eBPF C program using `bpf2go`. ## Compile eBPF C and generate scaffolding using bpf2go With the `counter.c` source file in place, create another file called `gen.go` containing a `//go:generate` statement. This invokes `bpf2go` when running `go generate` in the project directory. Aside from compiling our eBPF C program, bpf2go will also generate some scaffolding code we'll use to load our eBPF program into the kernel and interact with its various components. This greatly reduces the amount of code we need to write to get up and running. {{ go_example('getting_started_gen', title='gen.go') }} !!! tip "" Using a dedicated file for your package's `//go:generate` statement(s) is neat for keeping them separated from application logic. At this point in the guide, we don't have a `main.go` file yet. Feel free to include it in existing Go source files if you prefer. Before using the Go toolchain, Go wants us to declare a Go module. This command should take care of that: ```{ .shell-session data-copy="go mod init ebpf-test && go mod tidy" } % go mod init ebpf-test go: creating new go.mod: module ebpf-test go: to add module requirements and sums: go mod tidy % go mod tidy ``` First, add `bpf2go` as a tool dependency to your Go module. This ensures the version of `bpf2go` used by the Go toolchain always matches your version of the library. ```{ .shell-session data-copy="go get -tool github.com/cilium/ebpf/cmd/bpf2go" } % go get -tool github.com/cilium/ebpf/cmd/bpf2go ``` Now we're ready to run `go generate`: ```{ .shell-session data-copy="go generate" } % go generate Compiled /home/timo/getting_started/counter_bpfel.o Stripped /home/timo/getting_started/counter_bpfel.o Wrote /home/timo/getting_started/counter_bpfel.go Compiled /home/timo/getting_started/counter_bpfeb.o Stripped /home/timo/getting_started/counter_bpfeb.o Wrote /home/timo/getting_started/counter_bpfeb.go ``` `bpf2go` built `counter.c` into `counter_bpf*.o` behind the scenes using `clang`. It generated two object files and two corresponding Go source files based on the contents of the object files. Do not remove any of these, we'll need them later. Let's inspect one of the generated .go files: {{ go_example('counterPrograms', title='counter_bpfel.go', signature=True) }} Neat! Looks like bpf2go automatically generated a scaffolding for interacting with our `count_packets` Program from Go. In the next step, we'll explore how to load our program into the kernel and put it to work by attaching it to an XDP hook! ## The Go application Finally, with our eBPF C code compiled and Go scaffolding generated, all that's left is writing the Go code responsible for loading and attaching the program to a hook in the Linux kernel. Click the :material-plus-circle: annotations in the code snippet for some of the more intricate details. Note that we won't cover anything related to the Go standard library here. {{ go_example('getting_started_main', title='main.go') }} 1. Linux kernels before 5.11 use RLIMIT_MEMLOCK to control the maximum amount of memory allocated for a process' eBPF resources. By default, it's set to a relatively low value. See [Resource Limits](../concepts/rlimit.md) for a deep dive. 1. `counterObjects` is a struct containing nil pointers to Map and Program objects. A subsequent call to `loadCounterObjects` populates these fields based on the struct tags declared on them. This mechanism saves a lot of repetition that would occur by checking a Collection for Map and Program objects by string.

    As an added bonus, `counterObjects` adds type safety by turning these into compile-time lookups. If a Map or Program doesn't appear in the ELF, it won't appear as a struct field and your Go application won't compile, eliminating a whole class of runtime errors. 1. Close all file descriptors held by `objs` right before the Go application terminates. See [Object Lifecycle](../concepts/object-lifecycle.md) for a deep dive. 1. Associate the `count_packets` (stylized in the Go scaffolding as `CountPackets`) eBPF program with `eth0`. This returns a {{ godoc('link/Link') }} abstraction. 1. Close the file descriptor of the Program-to-interface association. Note that this will stop the Program from executing on incoming packets if the Link was not {{ godoc('link/Link.Pin') }}ed to the bpf file system. 1. Load a uint64 stored at index 0 from the `pkt_count` Map (stylized in the Go scaffolding as `PktCount`). This corresponds to the logic in `counter.c`. Save this file as `main.go` in the same directory alongside `counter.c` and `gen.go`. ## Building and running the Go application Now `main.go` is in place, we can finally compile and run our Go application! ```{ .shell-session data-copy="go build && sudo ./ebpf-test" } % go build && sudo ./ebpf-test 2023/09/20 17:18:43 Counting incoming packets on eth0.. 2023/09/20 17:18:47 Received 0 packets 2023/09/20 17:18:48 Received 4 packets 2023/09/20 17:18:49 Received 11 packets 2023/09/20 17:18:50 Received 15 packets ``` Generate some traffic on eth0 and you should see the counter increase. ### Iteration Workflow When iterating on the C code, make sure to keep generated files up-to-date. Without re-running bpf2go, the eBPF C won't be recompiled, and any changes made to the C program structure won't be reflected in the Go scaffolding. ```{ .shell-session data-copy="go generate && go build && sudo ./ebpf-test" } % go generate && go build && sudo ./ebpf-test ``` ## What's Next? Congratulations, you've just built your (presumably) first eBPF-powered Go app! Hopefully, this guide piqued your interest and gave you a better sense of what eBPF can do and how it works. With XDP, we've only barely scratched the surface of eBPF's many use cases and applications. For more easily-accessible examples, see [the main repository's examples/ folder](https://github.com/cilium/ebpf/tree/main/examples). It demonstrates use cases like tracing user space applications, extracting information from the kernel, attaching eBPF programs to network sockets and more. Follow our other guides to continue on your journey of shipping a portable eBPF-powered application to your users. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/guides/portable-ebpf.md000066400000000000000000000061671520243672000254010ustar00rootroot00000000000000# Shipping Portable eBPF-powered Applications !!! incomplete This guide builds on Getting Started. Document what the various ways are for making tools portable across kernel versions and what the various CO-RE techniques are. !!! tip "" We recommend building eBPF C code from within a container with a stable LLVM toolchain, as well as checking all generated `.o` and `.go` files into source control. This buys you fully-reproducible builds, prevents bugs due to team members using different LLVM versions and makes your packages fully independent and `go run`nable. It also prevents PII from leaking into ELFs in the form of absolute paths to `.c` source files in DWARF info. ### Cross-compiling You may have noticed bpf2go generating two sets of files: - `*_bpfel.o` and `*_bpfel.go` for little-endian architectures like amd64, arm64, riscv64 and loong64 - `*_bpfeb.o` and `*_bpfeb.go` for big-endian architectures like s390(x), mips and sparc Both sets of .go files contain a `//go:embed` statement that slurps the contents of the respective .o files into a byte slice at compile time. The result is a standalone Go application binary that can be deployed to a target machine without any of the .o files included. To further reduce runtime dependencies, add `CGO_ENABLED=0` to `go build` and your application won't depend on libc. (assuming none of your other dependencies require cgo) Moreover, because both eBPF objects and Go scaffolding are generated for both big- and little-endian architectures, cross-compiling your Go application is as simple as setting the right `GOARCH` value at compile time. Pulling it all together, for building an eBPF-powered Go application for a Raspberry Pi running a 64-bit Linux distribution: ```shell-session CGO_ENABLED=0 GOARCH=arm64 go build ``` ### Compile Once - Run Everywhere? Since we can generate a standalone binary and deploy it to any system, does that mean tools built using {{ proj }} will magically work anywhere? Unfortunately, no, not really. The kernel's internal data structures change as the kernel progresses in development, just like any other software. Differences in compile-time configuration affect data structures and the presence of certain kernel symbols. This means that, even when using the exact same kernel release, no two Linux distributions will be the same when it comes to data layout. This is problematic for authors that want to ship a single binary to their users and expect it to work across multiple distributions and kernel versions. In response to this, the term *Compile Once - Run Everywhere* was coined to describe the collection of techniques employed to achieve universal interoperability for eBPF. This technique relies on type information encoded in BPF Type Format (BTF) to be shipped with the kernel so memory accesses can be adjusted right before loading the eBPF program into the kernel. Alternatively, you may opt for shipping a full LLVM compiler toolchain along with your application and recompiling the eBPF C against Linux kernel headers present on the target machine. This approach is out of scope of the {{ proj }} documentation. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/guides/windows-support.md000066400000000000000000000045021520243672000260520ustar00rootroot00000000000000# Windows support The library has preliminary support for the [eBPF for Windows] runtime, allowing you to build Go applications for Windows using the same APIs as on Linux. !!! warning "Feature parity" efW doesn't have feature parity with Linux. Many APIs in the library will return `ErrNotSupported` in this case. !!! warning "Binary compatibility" efW is not binary compatible with Linux. It is not possible to compile an eBPF program for Linux and use it on Windows. ## Platform specific constants efW only provides [source compatibility] with Linux. While certain Linux map or program types have an equivalent on Windows, they don't always behave the same. For this reason, the various type enumerations have completely distinct values on Windows, for example `WindowsHashMap` is the equivalent of `HashMap`. Attempting to create a `HashMap` on Windows will return an error, and vice versa. ## Platform specific ELFs !!! note "" Loading Windows ELFs is not supported yet. ELFs compiled against Linux and Windows headers are not binary compatible. Add the following to ELFs targeting Windows until there is an [official way to declare the platform](https://github.com/microsoft/ebpf-for-windows/issues/3956): ```C const bool __ebpf_for_windows_tag __attribute__((section(".ebpf_for_windows"))) = true; ``` ## Working with signed programs The runtime will most likely require all eBPF programs to be signed by Microsoft. Signing programs relies on packaging eBPF `.c` files as drivers using the [native code pipeline], converting bytecode into a `.sys` file. The interface to load such drivers does not allow modifying the bytecode or map definitions, therefore you can't interact with them via `CollectionSpec`, etc. Instead you must load them via `LoadCollection`: ```go coll, err := LoadCollection("path\\to\\driver.sys") ``` The returned Collection contains Maps and Programs which you can interact with as usual. [eBPF for Windows]: https://github.com/microsoft/ebpf-for-windows [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 [native code pipeline]: https://github.com/microsoft/ebpf-for-windows/blob/main/docs/NativeCodeGeneration.md [LoadCollection]: https://pkg.go.dev/github.com/cilium/ebpf#LoadCollection golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/index.md000066400000000000000000000026531520243672000225020ustar00rootroot00000000000000

    The eBPF Library for Go

    ![Honeygopher](ebpf-go.png){ align=right width="180" } [![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf) :ebpf-go: {{ proj }} is a Go library for working with :ebee-color: eBPF. It does not depend on C, libbpf, or any other Go libraries other than the standard library, making it an excellent choice for writing self-contained, portable tools that run on a variety of architectures. This documentation aims to provide a central resource for learning how to build Go applications that use eBPF. ## Installing To add {{ proj }} as a dependency to an existing Go module, run this from within the module's directory: ``` go get github.com/cilium/ebpf ``` ## Target Audience This documentation assumes familiarity with the basic concepts and terminology of eBPF, as well as a basic understanding of the Go toolchain and how to write idiomatic Go code. For a high-level understanding of what eBPF is and how it works, please see [the eBPF introduction at :ebee-color: ebpf.io](https://ebpf.io/what-is-ebpf). ## Examples Discover [projects using {{ proj }} here](users.md). The repository contains an [examples/ directory](https://github.com/cilium/ebpf/tree/main/examples) with minimal demo applications that can be tested on any supported Linux machine. golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/stylesheets/000077500000000000000000000000001520243672000234175ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/stylesheets/extra.css000066400000000000000000000045531520243672000252630ustar00rootroot00000000000000/* Tagline on landing page. */ .tagline { font-size: 3em; font-weight: 900; letter-spacing: -0.5px; background: linear-gradient(120deg, #4051B5, 35%, #6AD6E4); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; } /* :progress-wrench: Custom 'incomplete' admonition for sections that need work or maintenance. Create blocks using '!!! incomplete'. */ :root { --md-admonition-icon--incomplete: url('data:image/svg+xml;charset=utf-8,') } .md-typeset .admonition.incomplete, .md-typeset details.incomplete { border-color: rgb(255, 204, 77); } .md-typeset .incomplete>.admonition-title, .md-typeset .incomplete>summary { background-color: rgba(255, 204, 77, 0.1); } .md-typeset .incomplete>.admonition-title::before, .md-typeset .incomplete>summary::before { background-color: rgb(255, 204, 77); -webkit-mask-image: var(--md-admonition-icon--incomplete); mask-image: var(--md-admonition-icon--incomplete); } /* gp and go are the classes used for prompt and output in shell-session code blocks. Prevent these from being highlighted as it hurts UX. */ .highlight .gp, .highlight .go { user-select: none; } .md-typeset { .md-badge { font-size: 0.85em; .md-badge__icon { padding: 0.4em; background: var(--md-accent-fg-color--transparent); border-start-start-radius: 0.1em; border-end-start-radius: 0.1em; } .md-badge__text { padding: 0.4em 0.8em; border-start-end-radius: 0.1em; border-end-end-radius: 0.1em; box-shadow: 0 0 0 1px inset var(--md-accent-fg-color--transparent); } } .md-badge--right { float: right; margin-left: 0.35em; } } golang-github-cilium-ebpf-0.21.0+ds1/docs/ebpf/users.md000066400000000000000000000062001520243672000225240ustar00rootroot00000000000000# Projects built with {{ proj }} Below is a non-comprehensive list of open-source software built with {{ proj }}, just for inspiration or to gain a better understanding of how to tackle certain problems using eBPF. A list of :fontawesome-brands-golang: {{ proj }} importers can be found on [Sourcegraph]. If you'd like to include a project on this page, feel free to open a pull request. [`Cilium`](https://github.com/cilium/cilium) : Kubernetes-oriented Container Networking Interface implementation providing network policy and observability. [`containerd`](https://github.com/containerd/cgroups) & [`runc`](https://github.com/opencontainers/runc) : Used by Docker and podman, these use eBPF for implementing device filters in cgroups. [`coroot`](https://github.com/coroot/coroot) : Zero-instrumentation observability featuring root cause analysis and anomaly detection. [`datadog-agent`](https://github.com/DataDog/datadog-agent) : The Datadog agent, the component responsible for collecting system and application metrics and shipping them to the Datadog platform. [`Delve`](https://github.com/go-delve/delve) : A debugger for the Go programming language. Uses eBPF uprobes for tracing user space code execution. [`gVisor`](https://github.com/google/gvisor) : gVisor relies on eBPF for implementing various forms of guest/workload isolation and security. [`Inspektor Gadget`](https://github.com/inspektor-gadget/inspektor-gadget) : A collection of tools to debug and inspect Kubernetes resources and applications. Reimplements many of the BCC tools for easy deployment onto a Kubernetes cluster. [`Istio`](https://github.com/istio/istio) : In Istio’s ambient mode, eBPF is used for redirecting application traffic to the zero-trust tunnel on the node. [`KubeArmor`](https://github.com/kubearmor/KubeArmor) : KubeArmor allows restricting the behaviour of Pods, containers and Kubernetes nodes at the system level. [`kube-proxy-ng`](https://github.com/kubernetes-sigs/kpng) : Emerging eBPF-based `kube-proxy` implementation, developed by the upstream Kubernetes project. [`OpenShift`](https://github.com/openshift/ingress-node-firewall) : OpenShift's ingress node firewall is implemented using eBPF. [`pwru`](https://github.com/cilium/pwru) : Packet, where are you? `tcpdump`, but for tracing a packet's journey through the kernel. [`Pyroscope`](https://github.com/grafana/pyroscope) : From Grafana, open source continuous profiling platform. Flame graphs! [`Tetragon`](https://github.com/cilium/tetragon) : eBPF-based security framework, also providing observability and runtime enforcement. [`Tubular`](https://github.com/cloudflare/tubular) : From Cloudflare, bind a service to any IP or port. See [the announcement blog post](https://blog.cloudflare.com/tubular-fixing-the-socket-api-with-ebpf/) for a deep dive into why it was created and how it works. [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 golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/000077500000000000000000000000001520243672000217455ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/docs.c000066400000000000000000000007251520243672000230450ustar00rootroot00000000000000//go:build ignore // DocMyMapProgram { #include #include // Declare a hash map called 'my_map' with a u32 key and a u64 value. // The __uint, __type and SEC macros are from libbpf's bpf_helpers.h. struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __u32); __type(value, __u64); __uint(max_entries, 1); } my_map SEC(".maps"); // Declare a dummy socket program called 'my_prog'. SEC("socket") int my_prog() { return 0; } // } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/docs_test.go000066400000000000000000000047011520243672000242650ustar00rootroot00000000000000//go:build linux package examples import ( "fmt" "github.com/cilium/ebpf" ) func DocLoadCollectionSpec() { // Parse an ELF into a CollectionSpec. // bpf_prog.o is the result of compiling BPF C code. spec, err := ebpf.LoadCollectionSpec("bpf_prog.o") if err != nil { panic(err) } // Look up the MapSpec and ProgramSpec in the CollectionSpec. m := spec.Maps["my_map"] p := spec.Programs["my_prog"] // Note: We've omitted nil checks for brevity, take a look at // LoadAndAssign for an automated way of checking for maps/programs. // Inspect the map and program type. fmt.Println(m.Type, p.Type) // Print the map's key and value BTF types. fmt.Println(m.Key, m.Value) // Print the program's instructions in a human-readable form, // similar to llvm-objdump -S. fmt.Println(p.Instructions) } func DocNewCollection() { spec, err := ebpf.LoadCollectionSpec("bpf_prog.o") if err != nil { panic(err) } // Instantiate a Collection from a CollectionSpec. coll, err := ebpf.NewCollection(spec) if err != nil { panic(err) } // Close the Collection before the enclosing function returns. defer coll.Close() // Obtain a reference to 'my_map'. m := coll.Maps["my_map"] // Set map key '1' to value '2'. if err := m.Put(uint32(1), uint64(2)); err != nil { panic(err) } } // DocLoadAndAssignObjs { type myObjs struct { MyMap *ebpf.Map `ebpf:"my_map"` MyProg *ebpf.Program `ebpf:"my_prog"` } func (objs *myObjs) Close() error { if err := objs.MyMap.Close(); err != nil { return err } if err := objs.MyProg.Close(); err != nil { return err } return nil } // } func DocLoadAndAssign() { spec, err := ebpf.LoadCollectionSpec("bpf_prog.o") if err != nil { panic(err) } // Insert only the resources specified in 'obj' into the kernel and assign // them to their respective fields. If any requested resources are not found // in the ELF, this will fail. Any errors encountered while loading Maps or // Programs will also be returned here. var objs myObjs if err := spec.LoadAndAssign(&objs, nil); err != nil { panic(err) } defer objs.Close() // Interact with MyMap through the custom struct. if err := objs.MyMap.Put(uint32(1), uint64(2)); err != nil { panic(err) } } func DocBTFTypeByName() { spec, err := ebpf.LoadCollectionSpec("bpf_prog.o") if err != nil { panic(err) } // Look up the __64 type declared in linux/bpf.h. t, err := spec.Types.AnyTypeByName("__u64") if err != nil { panic(err) } fmt.Println(t) } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/features_test.go000066400000000000000000000012621520243672000251520ustar00rootroot00000000000000//go:build linux package examples import ( "errors" "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/features" ) func DocDetectXDP() { err := features.HaveProgramType(ebpf.XDP) if errors.Is(err, ebpf.ErrNotSupported) { fmt.Println("XDP program type is not supported") return } if err != nil { // Feature detection was inconclusive. // // Note: always log and investigate these errors! These can be caused // by a lack of permissions, verifier errors, etc. Unless stated // otherwise, probes are expected to be conclusive. Please file // an issue if this is not the case in your environment. panic(err) } fmt.Println("XDP program type is supported") } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/000077500000000000000000000000001520243672000251345ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/counter.c000066400000000000000000000012031520243672000267530ustar00rootroot00000000000000// getting_started_counter { // (1)! //go:build ignore #include // (2)! #include struct { __uint(type, BPF_MAP_TYPE_ARRAY); // (3)! __type(key, __u32); __type(value, __u64); __uint(max_entries, 1); } pkt_count SEC(".maps"); // (4)! // count_packets atomically increases a packet counter on every invocation. SEC("xdp") // (5)! int count_packets() { __u32 key = 0; // (6)! __u64 *count = bpf_map_lookup_elem(&pkt_count, &key); // (7)! if (count) { // (8)! __sync_fetch_and_add(count, 1); // (9)! } return XDP_PASS; // (10)! } char __license[] SEC("license") = "Dual MIT/GPL"; // (11)! // } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/counter_bpfeb.go000066400000000000000000000062301520243672000303010ustar00rootroot00000000000000// Code generated by bpf2go; DO NOT EDIT. //go:build (mips || mips64 || ppc64 || s390x) && linux package main import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) // loadCounter returns the embedded CollectionSpec for counter. func loadCounter() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_CounterBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load counter: %w", err) } return spec, err } // loadCounterObjects loads counter and converts it into a struct. // // The following types are suitable as obj argument: // // *counterObjects // *counterPrograms // *counterMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadCounterObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadCounter() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // counterSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterSpecs struct { counterProgramSpecs counterMapSpecs counterVariableSpecs } // counterProgramSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterProgramSpecs struct { CountPackets *ebpf.ProgramSpec `ebpf:"count_packets"` } // counterMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterMapSpecs struct { PktCount *ebpf.MapSpec `ebpf:"pkt_count"` } // counterVariableSpecs contains global variables before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterVariableSpecs struct { } // counterObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterObjects struct { counterPrograms counterMaps counterVariables } func (o *counterObjects) Close() error { return _CounterClose( &o.counterPrograms, &o.counterMaps, ) } // counterMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterMaps struct { PktCount *ebpf.Map `ebpf:"pkt_count"` } func (m *counterMaps) Close() error { return _CounterClose( m.PktCount, ) } // counterVariables contains all global variables after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterVariables struct { } // counterPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterPrograms struct { CountPackets *ebpf.Program `ebpf:"count_packets"` } func (p *counterPrograms) Close() error { return _CounterClose( p.CountPackets, ) } func _CounterClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed counter_bpfeb.o var _CounterBytes []byte golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/counter_bpfeb.o000066400000000000000000000045101520243672000301310ustar00rootroot00000000000000ELF@@ c* Dual MIT/GPLp    , 2@  EJ@NT `  j S Xb h int__ARRAY_SIZE_TYPE____u32unsigned int__u64unsigned long longtypekeyvaluemax_entriespkt_countcount_packetsxdp./counter.cint count_packets() { __u32 key = 0; // (6)! __u64 *count = bpf_map_lookup_elem(&pkt_count, &key); // (7)! if (count) { // (8)! __sync_fetch_and_add(count, 1); // (9)! return XDP_PASS; // (10)!char__license.mapslicense lxx|D|H |L8|PH|TP|8`` H |,@P`p .text.rel.BTF.extpkt_countcount_packets.maps.relxdp.llvm_addrsig__license.strtab.symtab.rel.BTFRk@6@`2 @ , J f b @   @(p :oLZxgolang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/counter_bpfel.go000066400000000000000000000063231520243672000303160ustar00rootroot00000000000000// Code generated by bpf2go; DO NOT EDIT. //go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux package main import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) // loadCounter returns the embedded CollectionSpec for counter. func loadCounter() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_CounterBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load counter: %w", err) } return spec, err } // loadCounterObjects loads counter and converts it into a struct. // // The following types are suitable as obj argument: // // *counterObjects // *counterPrograms // *counterMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadCounterObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadCounter() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // counterSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterSpecs struct { counterProgramSpecs counterMapSpecs counterVariableSpecs } // counterProgramSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterProgramSpecs struct { CountPackets *ebpf.ProgramSpec `ebpf:"count_packets"` } // counterMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterMapSpecs struct { PktCount *ebpf.MapSpec `ebpf:"pkt_count"` } // counterVariableSpecs contains global variables before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type counterVariableSpecs struct { } // counterObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterObjects struct { counterPrograms counterMaps counterVariables } func (o *counterObjects) Close() error { return _CounterClose( &o.counterPrograms, &o.counterMaps, ) } // counterMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterMaps struct { PktCount *ebpf.Map `ebpf:"pkt_count"` } func (m *counterMaps) Close() error { return _CounterClose( m.PktCount, ) } // counterVariables contains all global variables after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterVariables struct { } // counterPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign. type counterPrograms struct { CountPackets *ebpf.Program `ebpf:"count_packets"` } func (p *counterPrograms) Close() error { return _CounterClose( p.CountPackets, ) } func _CounterClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed counter_bpfel.o var _CounterBytes []byte golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/counter_bpfel.o000066400000000000000000000045101520243672000301430ustar00rootroot00000000000000ELF@@ cDual MIT/GPLp    , 2@  EJ@NT `  j S Xb h int__ARRAY_SIZE_TYPE____u32unsigned int__u64unsigned long longtypekeyvaluemax_entriespkt_countcount_packetsxdp./counter.cint count_packets() { __u32 key = 0; // (6)! __u64 *count = bpf_map_lookup_elem(&pkt_count, &key); // (7)! if (count) { // (8)! __sync_fetch_and_add(count, 1); // (9)! return XDP_PASS; // (10)!char__license.mapslicense lxx|D|H |L8|PH|TP|8`` H |,@P`p .text.rel.BTF.extpkt_countcount_packets.maps.relxdp.llvm_addrsig__license.strtab.symtab.rel.BTFRk@6@`2 @ , J f b @   @(p :LoZxgolang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/gen.go000066400000000000000000000001721520243672000262340ustar00rootroot00000000000000//go:build linux // getting_started_gen { package main //go:generate go tool bpf2go -tags linux counter counter.c // } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/getting_started/main.go000066400000000000000000000030061520243672000264060ustar00rootroot00000000000000//go:build linux // getting_started_main { package main import ( "log" "net" "os" "os/signal" "time" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/rlimit" ) func main() { // Remove resource limits for kernels <5.11. if err := rlimit.RemoveMemlock(); err != nil { // (1)! log.Fatal("Removing memlock:", err) } // Load the compiled eBPF ELF and load it into the kernel. var objs counterObjects // (2)! if err := loadCounterObjects(&objs, nil); err != nil { log.Fatal("Loading eBPF objects:", err) } defer objs.Close() // (3)! ifname := "eth0" // Change this to an interface on your machine. iface, err := net.InterfaceByName(ifname) if err != nil { log.Fatalf("Getting interface %s: %s", ifname, err) } // Attach count_packets to the network interface. link, err := link.AttachXDP(link.XDPOptions{ // (4)! Program: objs.CountPackets, Interface: iface.Index, }) if err != nil { log.Fatal("Attaching XDP:", err) } defer link.Close() // (5)! log.Printf("Counting incoming packets on %s..", ifname) // Periodically fetch the packet counter from PktCount, // exit the program when interrupted. tick := time.Tick(time.Second) stop := make(chan os.Signal, 5) signal.Notify(stop, os.Interrupt) for { select { case <-tick: var count uint64 err := objs.PktCount.Lookup(uint32(0), &count) // (6)! if err != nil { log.Fatal("Map lookup:", err) } log.Printf("Received %d packets", count) case <-stop: log.Print("Received signal, exiting..") return } } } // } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/rlimit_test.go000066400000000000000000000002611520243672000246320ustar00rootroot00000000000000//go:build linux package examples // DocRlimit { import "github.com/cilium/ebpf/rlimit" func init() { if err := rlimit.RemoveMemlock(); err != nil { panic(err) } } // } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/000077500000000000000000000000001520243672000237155ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/gen.go000066400000000000000000000001011520243672000250050ustar00rootroot00000000000000package main //go:generate go tool bpf2go variables variables.c golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/main.go000066400000000000000000000047631520243672000252020ustar00rootroot00000000000000package main import ( "fmt" "github.com/cilium/ebpf" ) func main() { DocVariablesSetConst() DocVariablesSetGlobal() } // Full example written to be displayed in its entirety, so is commented generously. func DocVariablesSetConst() { // Load the object file from disk using a bpf2go-generated scaffolding. spec, err := loadVariables() if err != nil { panicf("loading CollectionSpec: %s", err) } // Set the 'const_u32' variable to 42 in the CollectionSpec. want := uint32(42) // (1)! if err := spec.Variables["const_u32"].Set(want); err != nil { panicf("setting variable: %s", err) } // Load the CollectionSpec. // // Note: modifying spec.Variables after this point is ineffectual! // Modifying *Spec resources does not affect loaded/running BPF programs. var obj variablesPrograms if err := spec.LoadAndAssign(&obj, nil); err != nil { panicf("loading BPF program: %s", err) } fmt.Println("Running program with const_u32 set to", want) // Dry-run the BPF program with an empty context. ret, _, err := obj.ConstExample.Test(make([]byte, 15)) // (2)! if err != nil { panicf("running BPF program: %s", err) } if ret != want { panicf("unexpected return value %d", ret) } fmt.Println("BPF program returned", ret) // Output: // Running program with const_u32 set to 42 // BPF program returned 42 } func DocVariablesSetGlobal() { spec, err := loadVariables() if err != nil { panicf("loading CollectionSpec: %s", err) } // DocVariablesSetGlobalU16 { set := uint16(9000) if err := spec.Variables["global_u16"].Set(set); err != nil { panicf("setting variable: %s", err) } // } coll, err := ebpf.NewCollection(spec) if err != nil { panicf("loading BPF program: %s", err) } fmt.Println("Running program with global_u16 set to", set) // DocVariablesSetGlobalRun { for range 3 { ret, _, err := coll.Programs["global_example"].Test(make([]byte, 15)) if err != nil { panicf("running BPF program: %s", err) } fmt.Println("BPF program returned", ret) } // Output: // Running program with global_u16 set to 9000 // BPF program returned 9000 // BPF program returned 9001 // BPF program returned 9002 // } // DocVariablesGetGlobalU16 { var global_u16 uint16 if err := coll.Variables["global_u16"].Get(&global_u16); err != nil { panicf("getting variable: %s", err) } fmt.Println("Variable global_u16 is now", global_u16) // Output: // Variable global_u16 is now 9003 // } } func panicf(format string, args ...interface{}) { panic(fmt.Sprintf(format, args...)) } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/variables.c000066400000000000000000000010701520243672000260270ustar00rootroot00000000000000//go:build ignore #include #include // Remove when toolchain Docker image ships with 5.13+ headers. #define __hidden __attribute__((visibility("hidden"))) // variables_const { volatile const __u32 const_u32; SEC("socket") int const_example() { return const_u32; } // } // variables_global { volatile __u16 global_u16; SEC("socket") int global_example() { global_u16++; return global_u16; } // } // variables_hidden { __hidden __u64 hidden_var; SEC("socket") int hidden_example() { hidden_var++; return hidden_var; } // } golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/variables_bpfeb.go000066400000000000000000000071171520243672000273600ustar00rootroot00000000000000// Code generated by bpf2go; DO NOT EDIT. //go:build mips || mips64 || ppc64 || s390x package main import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) // loadVariables returns the embedded CollectionSpec for variables. func loadVariables() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_VariablesBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load variables: %w", err) } return spec, err } // loadVariablesObjects loads variables and converts it into a struct. // // The following types are suitable as obj argument: // // *variablesObjects // *variablesPrograms // *variablesMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadVariablesObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadVariables() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // variablesSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesSpecs struct { variablesProgramSpecs variablesMapSpecs variablesVariableSpecs } // variablesProgramSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesProgramSpecs struct { ConstExample *ebpf.ProgramSpec `ebpf:"const_example"` GlobalExample *ebpf.ProgramSpec `ebpf:"global_example"` HiddenExample *ebpf.ProgramSpec `ebpf:"hidden_example"` } // variablesMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesMapSpecs struct { } // variablesVariableSpecs contains global variables before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesVariableSpecs struct { ConstU32 *ebpf.VariableSpec `ebpf:"const_u32"` GlobalU16 *ebpf.VariableSpec `ebpf:"global_u16"` } // variablesObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesObjects struct { variablesPrograms variablesMaps variablesVariables } func (o *variablesObjects) Close() error { return _VariablesClose( &o.variablesPrograms, &o.variablesMaps, ) } // variablesMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesMaps struct { } func (m *variablesMaps) Close() error { return _VariablesClose() } // variablesVariables contains all global variables after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesVariables struct { ConstU32 *ebpf.Variable `ebpf:"const_u32"` GlobalU16 *ebpf.Variable `ebpf:"global_u16"` } // variablesPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesPrograms struct { ConstExample *ebpf.Program `ebpf:"const_example"` GlobalExample *ebpf.Program `ebpf:"global_example"` HiddenExample *ebpf.Program `ebpf:"hidden_example"` } func (p *variablesPrograms) Close() error { return _VariablesClose( p.ConstExample, p.GlobalExample, p.HiddenExample, ) } func _VariablesClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed variables_bpfeb.o var _VariablesBytes []byte golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/variables_bpfeb.o000066400000000000000000000046401520243672000272070ustar00rootroot00000000000000ELF`@@ ai! kiy{<<    ;  m      @ intconst_examplesocket./variables.c return const_u32;global_example global_u16++; return global_u16;hidden_example hidden_var++; return hidden_var;__u32unsigned intconst_u32__u16unsigned shortglobal_u16__u64unsigned long longhidden_var.bss.rodata $$| X(4 (4 JT HYX PYXX|x |= Z 8KX0$ X(4L,4<P`p  .text.rel.BTF.ext.relsocket.bsshidden_var.llvm_addrsigconst_examplehidden_exampleglobal_example.strtab.symtab.rodata.rel.BTFglobal_u16const_u32i@@ @0 ya @0  4 @ /oLqgolang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/variables_bpfel.go000066400000000000000000000072121520243672000273660ustar00rootroot00000000000000// Code generated by bpf2go; DO NOT EDIT. //go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm package main import ( "bytes" _ "embed" "fmt" "io" "github.com/cilium/ebpf" ) // loadVariables returns the embedded CollectionSpec for variables. func loadVariables() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_VariablesBytes) spec, err := ebpf.LoadCollectionSpecFromReader(reader) if err != nil { return nil, fmt.Errorf("can't load variables: %w", err) } return spec, err } // loadVariablesObjects loads variables and converts it into a struct. // // The following types are suitable as obj argument: // // *variablesObjects // *variablesPrograms // *variablesMaps // // See ebpf.CollectionSpec.LoadAndAssign documentation for details. func loadVariablesObjects(obj interface{}, opts *ebpf.CollectionOptions) error { spec, err := loadVariables() if err != nil { return err } return spec.LoadAndAssign(obj, opts) } // variablesSpecs contains maps and programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesSpecs struct { variablesProgramSpecs variablesMapSpecs variablesVariableSpecs } // variablesProgramSpecs contains programs before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesProgramSpecs struct { ConstExample *ebpf.ProgramSpec `ebpf:"const_example"` GlobalExample *ebpf.ProgramSpec `ebpf:"global_example"` HiddenExample *ebpf.ProgramSpec `ebpf:"hidden_example"` } // variablesMapSpecs contains maps before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesMapSpecs struct { } // variablesVariableSpecs contains global variables before they are loaded into the kernel. // // It can be passed ebpf.CollectionSpec.Assign. type variablesVariableSpecs struct { ConstU32 *ebpf.VariableSpec `ebpf:"const_u32"` GlobalU16 *ebpf.VariableSpec `ebpf:"global_u16"` } // variablesObjects contains all objects after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesObjects struct { variablesPrograms variablesMaps variablesVariables } func (o *variablesObjects) Close() error { return _VariablesClose( &o.variablesPrograms, &o.variablesMaps, ) } // variablesMaps contains all maps after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesMaps struct { } func (m *variablesMaps) Close() error { return _VariablesClose() } // variablesVariables contains all global variables after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesVariables struct { ConstU32 *ebpf.Variable `ebpf:"const_u32"` GlobalU16 *ebpf.Variable `ebpf:"global_u16"` } // variablesPrograms contains all programs after they have been loaded into the kernel. // // It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign. type variablesPrograms struct { ConstExample *ebpf.Program `ebpf:"const_example"` GlobalExample *ebpf.Program `ebpf:"global_example"` HiddenExample *ebpf.Program `ebpf:"hidden_example"` } func (p *variablesPrograms) Close() error { return _VariablesClose( p.ConstExample, p.GlobalExample, p.HiddenExample, ) } func _VariablesClose(closers ...io.Closer) error { for _, closer := range closers { if err := closer.Close(); err != nil { return err } } return nil } // Do not access this directly. // //go:embed variables_bpfel.o var _VariablesBytes []byte golang-github-cilium-ebpf-0.21.0+ds1/docs/examples/variables/variables_bpfel.o000066400000000000000000000046401520243672000272210ustar00rootroot00000000000000ELF`@@ aik!iy{<<     ;  m      @ intconst_examplesocket./variables.c return const_u32;global_example global_u16++; return global_u16;hidden_example hidden_var++; return hidden_var;__u32unsigned intconst_u32__u16unsigned shortglobal_u16__u64unsigned long longhidden_var.bss.rodata $$| X( 4(4 J THY XPYXX| x|= Z 8KX0$ X(4L,4<P`p  .text.rel.BTF.ext.relsocket.bsshidden_var.llvm_addrsigconst_examplehidden_exampleglobal_example.strtab.symtab.rodata.rel.BTFglobal_u16const_u32i@@ @0 ya @0  4 @ /Loqgolang-github-cilium-ebpf-0.21.0+ds1/docs/includes/000077500000000000000000000000001520243672000217355ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/includes/glossary.md000066400000000000000000000035651520243672000241330ustar00rootroot00000000000000 *[Program]: Instructions that can be loaded and attached to one or more hooks in the Linux kernel. *[Map]: Shared piece of memory between userspace and an eBPF program loaded into the kernel. *[Link]: Connection between a Program and a hook/event in the kernel. *[BTF]: BPF Type Format; a description of all data types present in the Linux kernel an eBPF object. *[ELF]: Executable and Linkable Format, a container format used for compiled eBPF programs. *[Spec]: Unrealized blueprint of an eBPF resource, e.g. MapSpec, ProgramSpec, btf.Spec. *[CollectionSpec]: Bundle of ProgramSpecs, MapSpecs and a btf.Spec. Direct result of loading an eBPF ELF. *[VariableSpec]: Accessor for a global variable declared in an eBPF program. *[Collection]: Bundle of Maps and Programs that were loaded into the kernel. Direct result of instantiating (loading into the kernel) a CollectionSpec. *[Variable]: Accessor for a global variable declared in an eBPF program, used after loading. *[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. *[helper]: A piece of logic provided by the kernel. Read a map value, redirect a packet, etc. *[kfunc]: An extensible evolution of the BPF helper mechanism. Can be dynamically provided by kernel modules. Not specified in UAPI. *[XDP]: eXpress Data Path, a high-performance eBPF-powered data path. Only has a receive hook. *[bpf2go]: Convenience utility to compile eBPF C using clang and generate a Go skeleton. *[libbpf]: A library for writing kernel- and user space BPF programs in C, developed by the upstream Linux project. *[qemu]: A popular virtual machine manager. *[DCO]: Developer Certificate of Origin. *[efW]: eBPF for Windows golang-github-cilium-ebpf-0.21.0+ds1/docs/macros.py000066400000000000000000000165101520243672000217700ustar00rootroot00000000000000"""Macro definitions for documentation.""" # Use built-in 'list' type when upgrading to Python 3.9. import glob import os import re import textwrap from io import TextIOWrapper from typing import List from urllib.parse import ParseResult, urlparse from mkdocs_macros.plugin import MacrosPlugin def define_env(env: MacrosPlugin): """ Define the mkdocs-macros-plugin environment. This function is called on setup. 'env' can be interacted with for defining variables, macros and filters. - variables: the dictionary that contains the environment variables - macro: a decorator function, to declare a macro. - filter: a function with one or more arguments, used to perform a transformation """ # Values can be overridden in mkdocs.yml:extras. go_examples_path: str = env.variables.get( "go_examples_path", "examples/**/*.go" ) godoc_url: ParseResult = urlparse( env.variables.get( "godoc_url", "https://pkg.go.dev/github.com/cilium/ebpf" ) ) c_examples_path: str = env.variables.get("c_examples_path", "examples/**/*.c") @env.macro def godoc(sym: str, short: bool = False): """ Generate a godoc link based on the configured godoc_url. `sym` is the symbol to link to. A dot '.' separator means it's a method on another type. Forward slashes '/' can be used to navigate to symbols in subpackages. For example: - CollectionSpec.LoadAndAssign - link/Link - btf/Spec.TypeByID `short` renders only the symbol name. """ if len(godoc_url) == 0: raise ValueError("Empty godoc url") # Support referring to symbols in subpackages. subpkg = os.path.dirname(sym) # Symbol name including dots for struct methods. (e.g. Map.Get) name = os.path.basename(sym) # Python's urljoin() expects the base path to have a trailing slash for # it to correctly append subdirs. Use urlparse instead, and interact # with the URL's components individually. url = godoc_url._replace( path=os.path.join(godoc_url.path, subpkg), # Anchor token appearing after the # in the URL. fragment=name, ).geturl() text = name if short: text = text.split(".")[-1] return f"[:fontawesome-brands-golang: `{text}`]({url})" @env.macro def go_example(*args, **kwargs): """ Include the body of a Go code example. See docstring of code_example() for details. """ return code_example( *args, **kwargs, language="go", path=go_examples_path ) @env.macro def c_example(*args, **kwargs): """ Include the body of a C code example. See docstring of `code_example` for details. """ return code_example( *args, **kwargs, language="c", path=c_examples_path ) @env.macro def linux_version(version: str, why: str = ''): """ Render a badge with the Linux logo and a version number denoting the minimum kernel version needed to use a feature. Optional string to explain why the feature won't work on older versions. """ return ('' # TODO: Make the icon link to some docs about handling kernel # versions, once those are written. ':simple-linux:' f'[{version}](# "{why}")' '') def code_example( symbol: str, title: str = None, language: str = "", lines: bool = True, signature: bool = False, path: str = "", ) -> str: """ Include the body of a code example. `symbol` takes the name of the function or snippet to include. `title` is rendered as a title at the top of the snippet. `language` is the name of the programming language passed to pygments. `lines` controls rendering line numbers. `signature` controls whether or not the function signature and brackets are included. `path` specifies the include path that may contain globs. """ opts: List[str] = [] if lines: opts.append("linenums='1'") if title: opts.append(f"title='{title}'") if signature: body = full_body(path, symbol) else: body = inner_body(path, symbol) out = f"``` {language} {' '. join(opts)}\n{body}```" return out def inner_body(path: str, sym: str) -> str: """ Get the inner body of sym, using default delimiters. First and last lines (so, function signature and closing bracket) are stripped, the remaining body dedented. """ out = _search_body(path, sym) if len(out) < 2: raise ValueError( f"Need at least two lines to get inner body for symbol {sym}" ) return textwrap.dedent("".join(out[1:-1])) def full_body(path: str, sym: str) -> str: """Get the full body of sym, using default delimiters, dedented.""" out = _search_body(path, sym) return textwrap.dedent("".join(out)) def _get_body( f: TextIOWrapper, sym: str, start: str = "{", end: str = "}" ) -> List[str]: """ Extract a body of text between sym and start/end delimiters. Tailored to finding function bodies of C-family programming languages with curly braces. The starting line of the body must contain sym prefixed by a space, with 'start' appearing on the same line, for example " Foo() {". Further occurrences of "{" and its closing counterpart "}" are tracked, and the lines between and including the final "}" are returned. """ found = False stack = 0 lines = [] for line in f.readlines(): if not found: # Skip current line if we're not in a body and the current line # doesn't contain the given symbol. # # The symbol must be surrounded by non-word characters like spaces # or parentheses. For example, a line "// DocObjs {" or "func # DocLoader() {" should match. if re.search(rf"\W{sym}\W", line) is None: continue found = True # Count the amount of start delimiters. stack += line.count(start) if stack == 0: # No opening delimiter found, ignore the line. found = False continue lines.append(line) # Count the amount of end delimiters and stop if we've escaped the # current scope. stack -= line.count(end) if stack <= 0: break # Rewind the file for reuse. f.seek(0) if stack > 0: raise LookupError(f"No end delimiter for {sym}") if len(lines) == 0: raise LookupError(f"Symbol {sym} not found") return lines def _search_body(path: str, sym: str) -> List[str]: """Find the body of the given symbol in a path glob.""" files = glob.glob(path, recursive=True) if len(files) == 0: raise LookupError(f"Path {path} did not match any files") for file in files: with open(file, mode="r") as f: try: return _get_body(f, sym) except LookupError: continue raise LookupError(f"Symbol {sym} not found in any of {files}") golang-github-cilium-ebpf-0.21.0+ds1/docs/mkdocs.yml000066400000000000000000000110131520243672000221260ustar00rootroot00000000000000site_name: "ebpf-go Documentation" site_description: Pure-Go library to read, modify and load eBPF programs and attach them to various hooks in the Linux kernel. site_author: Cilium Community # Rendered in header. repo_url: https://github.com/cilium/ebpf repo_name: cilium/ebpf edit_uri: edit/main/docs/ebpf/ # Directory to look for Markdown files within docs/. docs_dir: ebpf theme: logo: ebpf-go.png favicon: ebpf-go.png name: material icon: # GitHub link in the header. repo: fontawesome/brands/github-alt # Edit button at the top of each page. edit: material/pencil-ruler features: # Display sections in the navbar. - navigation.sections # Anchor tracking, updates the address bar with the active anchor. - navigation.tracking # Use XHR instead of fully reloading the page when navigating around. - nagivation.instant # Clipboard button in code blocks. - content.code.copy # Enable annotations in code blocks. - content.code.annotate # Button to edit page on GitHub. - content.action.edit # Better (faster) tooltips, replacing the browser's rendering logic. - content.tooltips palette: # Palette toggle for light mode - media: "(prefers-color-scheme: light)" scheme: default toggle: icon: material/lightbulb-off name: Switch to dark mode # Palette toggle for dark mode - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/lightbulb-on name: Switch to light mode # Template overrides. custom_dir: overrides nav: - 'Home': index.md - 'Guides': - 'Getting Started': guides/getting-started.md - 'Portable eBPF': guides/portable-ebpf.md - 'Windows support': guides/windows-support.md - 'Concepts': - 'Loading eBPF Programs': concepts/loader.md - 'Global Variables': concepts/global-variables.md - 'Resource Limits': concepts/rlimit.md - 'Section Naming': concepts/section-naming.md - 'Feature Detection': concepts/features.md - 'Object Lifecycle': concepts/object-lifecycle.md - 'Contributing': - contributing/index.md - contributing/architecture.md - contributing/new-feature.md - contributing/new-example.md - contributing/windows.md - 'Users': users.md - 'Go Reference': https://pkg.go.dev/github.com/cilium/ebpf - 'GitHub': - 'Repository': https://github.com/cilium/ebpf - 'Issue Tracker': https://github.com/cilium/ebpf/issues - 'Discussions': https://github.com/cilium/ebpf/discussions - 'About': about.md extra: social: - icon: fontawesome/brands/github link: https://github.com/cilium/ebpf extra_css: - stylesheets/extra.css watch: - examples/ - includes/ - overrides/ - macros.py plugins: - search - macros: # This opens macros.py in docs/. module_name: macros # Make the mkdocs build fail if any errors occur. # Otherwise, any errors would be rendered to the build output. on_error_fail: true include_yaml: - vars.yml # Updated/authors displayed in footer. # Layout is customized in overrides/partials/source-file.html. - git-revision-date-localized: type: timeago - git-authors: show_email_address: false authorship_threshold_percent: 10 exclude: - index.md # Enable syntax highlighting in mkdocs-material. markdown_extensions: # Automatic tooltips for abbreviations/glossary. - abbr # Setting attributes on code fences (e.g. ``` go linenums='1') - attr_list # Special content blocks like '!!! note' - admonition # Definition lists using indented descriptions - def_list - footnotes # Collapsible admonitions - pymdownx.details # Syntax highlighting in code blocks - pymdownx.highlight: anchor_linenums: true - pymdownx.inlinehilite # Glossary - pymdownx.snippets: auto_append: - includes/glossary.md # Superfences enables nested and tabbed code blocks and Mermaid support - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid format: !!python/name:pymdownx.superfences.fence_code_format # Content tabs for code snippets, checklists, etc. - pymdownx.tabbed: alternate_style: true # Emoji and icons like :fontawesome-brands-golang: - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg options: custom_icons: - overrides/.icons # Table of Contents - toc: permalink: true golang-github-cilium-ebpf-0.21.0+ds1/docs/overrides/000077500000000000000000000000001520243672000221315ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/overrides/.icons/000077500000000000000000000000001520243672000233225ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/overrides/.icons/ebee-color.svg000066400000000000000000000114531520243672000260630ustar00rootroot00000000000000 golang-github-cilium-ebpf-0.21.0+ds1/docs/overrides/.icons/ebee-mono.svg000066400000000000000000000060741520243672000257200ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/overrides/.icons/ebpf-go.svg000066400000000000000000002245521520243672000253740ustar00rootroot00000000000000 golang-github-cilium-ebpf-0.21.0+ds1/docs/overrides/partials/000077500000000000000000000000001520243672000237505ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/docs/overrides/partials/source-file.html000066400000000000000000000004361520243672000270560ustar00rootroot00000000000000
    {% if page.meta.git_revision_date_localized %} Last updated {{ page.meta.git_revision_date_localized }} {% endif %}
    {% if git_page_authors %} Authored by {{ git_page_authors }} {% endif %}
    golang-github-cilium-ebpf-0.21.0+ds1/docs/vars.yml000066400000000000000000000001341520243672000216230ustar00rootroot00000000000000# Variables accessible in documentation using e.g. '{{ proj }}'. extra: proj: "`ebpf-go`" golang-github-cilium-ebpf-0.21.0+ds1/elf_reader.go000066400000000000000000001372131520243672000216250ustar00rootroot00000000000000package ebpf import ( "bufio" "bytes" "debug/elf" "encoding/binary" "errors" "fmt" "io" "iter" "maps" "math" "os" "slices" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" ) type kconfigMetaKey struct{} type kconfigMeta struct { Map *MapSpec Offset uint32 } type kfuncMetaKey struct{} type kfuncMeta struct { Binding elf.SymBind Func *btf.Func } type ksymMetaKey struct{} type ksymMeta struct { Binding elf.SymBind Name string } // elfCode is a convenience to reduce the amount of arguments that have to // be passed around explicitly. You should treat its contents as immutable. type elfCode struct { *internal.SafeELFFile sections map[elf.SectionIndex]*elfSection license string version uint32 btf *btf.Spec extInfo *btf.ExtInfos maps map[string]*MapSpec vars map[string]*VariableSpec kfuncs map[string]*btf.Func ksyms map[string]struct{} kconfig *MapSpec } // LoadCollectionSpec parses an ELF file into a CollectionSpec. func LoadCollectionSpec(file string) (*CollectionSpec, error) { f, err := os.Open(file) if err != nil { return nil, err } defer f.Close() spec, err := LoadCollectionSpecFromReader(f) if err != nil { return nil, fmt.Errorf("file %s: %w", file, err) } return spec, nil } // LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec. func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { f, err := internal.NewSafeELFFile(rd) if err != nil { return nil, err } // Checks if the ELF file is for BPF data. // Old LLVM versions set e_machine to EM_NONE. if f.Machine != elf.EM_NONE && f.Machine != elf.EM_BPF { return nil, fmt.Errorf("unexpected machine type for BPF ELF: %s", f.Machine) } var ( licenseSection *elf.Section versionSection *elf.Section sections = make(map[elf.SectionIndex]*elfSection) relSections = make(map[elf.SectionIndex]*elf.Section) ) // This is the target of relocations generated by inline assembly. sections[elf.SHN_UNDEF] = newElfSection(new(elf.Section), undefSection) // Collect all the sections we're interested in. This includes relocations // which we parse later. // // Keep the documentation at docs/ebpf/loading/elf-sections.md up-to-date. for i, sec := range f.Sections { idx := elf.SectionIndex(i) switch { case strings.HasPrefix(sec.Name, "license"): licenseSection = sec case strings.HasPrefix(sec.Name, "version"): versionSection = sec case strings.HasPrefix(sec.Name, "maps"): sections[idx] = newElfSection(sec, mapSection) case sec.Name == ".maps": sections[idx] = newElfSection(sec, btfMapSection) case isDataSection(sec.Name): sections[idx] = newElfSection(sec, dataSection) case sec.Type == elf.SHT_REL: // Store relocations under the section index of the target relSections[elf.SectionIndex(sec.Info)] = sec case sec.Type == elf.SHT_PROGBITS && sec.Size > 0: if (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0 { sections[idx] = newElfSection(sec, programSection) } else if sec.Name == structOpsLinkSec { // classification based on sec names so that struct_ops-specific // sections (.struct_ops.link) is correctly recognized // as non-executable PROGBITS, allowing value placement and link metadata to be loaded. sections[idx] = newElfSection(sec, structOpsSection) } else if sec.Name == structOpsSec { return nil, fmt.Errorf("section %q: got '.struct_ops' section: %w", sec.Name, ErrNotSupported) } } } license, err := loadLicense(licenseSection) if err != nil { return nil, fmt.Errorf("load license: %w", err) } version, err := loadVersion(versionSection, f.ByteOrder) if err != nil { return nil, fmt.Errorf("load version: %w", err) } btfSpec, btfExtInfo, err := btf.LoadSpecAndExtInfosFromReader(rd) if err != nil && !errors.Is(err, btf.ErrNotFound) { return nil, fmt.Errorf("load BTF: %w", err) } ec := &elfCode{ SafeELFFile: f, sections: sections, license: license, version: version, btf: btfSpec, extInfo: btfExtInfo, maps: make(map[string]*MapSpec), vars: make(map[string]*VariableSpec), kfuncs: make(map[string]*btf.Func), ksyms: make(map[string]struct{}), } symbols, err := f.Symbols() if err != nil { return nil, fmt.Errorf("load symbols: %v", err) } ec.assignSymbols(symbols) if err := ec.loadRelocations(relSections, symbols); err != nil { return nil, fmt.Errorf("load relocations: %w", err) } if err := ec.loadMaps(); err != nil { return nil, fmt.Errorf("load maps: %w", err) } if err := ec.loadBTFMaps(); err != nil { return nil, fmt.Errorf("load BTF maps: %w", err) } if err := ec.loadDataSections(); err != nil { return nil, fmt.Errorf("load data sections: %w", err) } if err := ec.loadKconfigSection(); err != nil { return nil, fmt.Errorf("load virtual .kconfig section: %w", err) } if err := ec.loadKsymsSection(); err != nil { return nil, fmt.Errorf("load virtual .ksyms section: %w", err) } // Finally, collect programs and link them. progs, err := ec.loadProgramSections() if err != nil { return nil, fmt.Errorf("load programs: %w", err) } // assiociate members in structs with ProgramSpecs using relo if err := ec.associateStructOpsRelocs(progs); err != nil { return nil, fmt.Errorf("load struct_ops: %w", err) } return &CollectionSpec{ ec.maps, progs, ec.vars, btfSpec, ec.ByteOrder, }, nil } func loadLicense(sec *elf.Section) (string, error) { if sec == nil { return "", nil } data, err := sec.Data() if err != nil { return "", fmt.Errorf("section %s: %v", sec.Name, err) } return string(bytes.TrimRight(data, "\000")), nil } func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { if sec == nil { return 0, nil } var version uint32 if err := binary.Read(sec.Open(), bo, &version); err != nil { return 0, fmt.Errorf("section %s: %v", sec.Name, err) } return version, nil } func isDataSection(name string) bool { return name == ".bss" || strings.HasPrefix(name, ".data") || strings.HasPrefix(name, ".rodata") } func isConstantDataSection(name string) bool { return strings.HasPrefix(name, ".rodata") } func isKconfigSection(name string) bool { return name == ".kconfig" } type elfSectionKind int const ( undefSection elfSectionKind = iota mapSection btfMapSection programSection dataSection structOpsSection ) type elfSection struct { *elf.Section kind elfSectionKind // Offset from the start of the section to a symbol symbols map[uint64]elf.Symbol // Offset from the start of the section to a relocation, which points at // a symbol in another section. relocations map[uint64]elf.Symbol // The number of relocations pointing at this section. references int } func newElfSection(section *elf.Section, kind elfSectionKind) *elfSection { return &elfSection{ section, kind, make(map[uint64]elf.Symbol), make(map[uint64]elf.Symbol), 0, } } // symbolsSorted returns the section's symbols sorted by offset. func (es *elfSection) symbolsSorted() iter.Seq2[uint64, elf.Symbol] { return func(yield func(uint64, elf.Symbol) bool) { for _, off := range slices.Sorted(maps.Keys(es.symbols)) { if !yield(off, es.symbols[off]) { return } } } } // assignSymbols takes a list of symbols and assigns them to their // respective sections, indexed by name. func (ec *elfCode) assignSymbols(symbols []elf.Symbol) { for _, symbol := range symbols { symType := elf.ST_TYPE(symbol.Info) symSection := ec.sections[symbol.Section] if symSection == nil { continue } // Anonymous symbols only occur in debug sections which we don't process // relocations for. Anonymous symbols are not referenced from other sections. if symbol.Name == "" { continue } // Older versions of LLVM don't tag symbols correctly, so keep // all NOTYPE ones. switch symSection.kind { case mapSection, btfMapSection, dataSection: if symType != elf.STT_NOTYPE && symType != elf.STT_OBJECT { continue } case programSection: if symType != elf.STT_NOTYPE && symType != elf.STT_FUNC { continue } // Program sections may contain NOTYPE symbols with local scope, these are // usually labels for jumps. We do not care for these for the purposes of // linking and they may overlap with function symbols. if symType == elf.STT_NOTYPE && elf.ST_BIND(symbol.Info) == elf.STB_LOCAL { continue } // Only collect symbols that occur in program/maps/data sections. default: continue } symSection.symbols[symbol.Value] = symbol } } // loadRelocations iterates .rel* sections and extracts relocation entries for // sections of interest. Makes sure relocations point at valid sections. func (ec *elfCode) loadRelocations(relSections map[elf.SectionIndex]*elf.Section, symbols []elf.Symbol) error { for idx, relSection := range relSections { section := ec.sections[idx] if section == nil { continue } rels, err := ec.loadSectionRelocations(relSection, symbols) if err != nil { return fmt.Errorf("relocation for section %q: %w", section.Name, err) } for _, rel := range rels { target := ec.sections[rel.Section] if target == nil { return fmt.Errorf("section %q: reference to %q in section %s: %w", section.Name, rel.Name, rel.Section, ErrNotSupported) } target.references++ } section.relocations = rels } return nil } // loadProgramSections iterates ec's sections and emits a ProgramSpec // for each function it finds. // // The resulting map is indexed by function name. func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) { progs := make(map[string]*ProgramSpec) // Generate a ProgramSpec for each function found in each program section. var export []string for _, sec := range ec.sections { if sec.kind != programSection { continue } if len(sec.symbols) == 0 { return nil, fmt.Errorf("section %v: missing symbols", sec.Name) } funcs, err := ec.loadFunctions(sec) if err != nil { return nil, fmt.Errorf("section %v: %w", sec.Name, err) } progType, attachType, progFlags, attachTo := getProgType(sec.Name) for name, insns := range funcs { spec := &ProgramSpec{ Name: name, Type: progType, Flags: progFlags, AttachType: attachType, AttachTo: attachTo, SectionName: sec.Name, License: ec.license, KernelVersion: ec.version, Instructions: insns, ByteOrder: ec.ByteOrder, } // Function names must be unique within a single ELF blob. if progs[name] != nil { return nil, fmt.Errorf("duplicate program name %s", name) } progs[name] = spec if spec.SectionName != ".text" { export = append(export, name) } } } flattenPrograms(progs, export) // Hide programs (e.g. library functions) that were not explicitly emitted // to an ELF section. These could be exposed in a separate CollectionSpec // field later to allow them to be modified. for n, p := range progs { if p.SectionName == ".text" { delete(progs, n) } } return progs, nil } // loadFunctions extracts instruction streams from the given program section // starting at each symbol in the section. The section's symbols must already // be narrowed down to STT_NOTYPE (emitted by clang <8) or STT_FUNC. // // The resulting map is indexed by function name. func (ec *elfCode) loadFunctions(sec *elfSection) (map[string]asm.Instructions, error) { progs := make(map[string]asm.Instructions) // Pull out ExtInfos once per section to avoid map lookups on every // instruction. fo, lo, ro := ec.extInfo.Section(sec.Name) // Raw instruction count since start of the section. ExtInfos point at raw // insn offsets and ignore the gaps between symbols in case of linked objects. // We need to count them, we can't obtain this info by any other means. var raw asm.RawInstructionOffset // Sort symbols by offset so we can track instructions by their raw offsets. for _, sym := range sec.symbolsSorted() { if progs[sym.Name] != nil { return nil, fmt.Errorf("duplicate symbol %s in section %s", sym.Name, sec.Name) } // Decode the symbol's instruction stream, limited to its size. sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size)) insns := make(asm.Instructions, 0, sym.Size/asm.InstructionSize) insns, err := asm.AppendInstructions(insns, sr, ec.ByteOrder, platform.Linux) if err != nil { return nil, fmt.Errorf("decoding instructions for symbol %s in section %s: %w", sym.Name, sec.Name, err) } if len(insns) == 0 { return nil, fmt.Errorf("no instructions found for symbol %s in section %s", sym.Name, sec.Name) } // Mark the first instruction as the start of a function. insns[0] = insns[0].WithSymbol(sym.Name) iter := insns.Iterate() for iter.Next() { // Global byte offset of the instruction within the ELF section. offset := sym.Value + iter.Offset.Bytes() // Apply any relocations for the current instruction. If no relocation is // present, resolve any section-relative function calls. if rel, ok := sec.relocations[offset]; ok { if err := ec.relocateInstruction(iter.Ins, rel); err != nil { return nil, fmt.Errorf("offset %d in section %s: relocating instruction: %w", offset, sec.Name, err) } } else { if err := referenceRelativeJump(iter.Ins, offset, sec.symbols); err != nil { return nil, fmt.Errorf("offset %d in section %s: resolving relative jump: %w", offset, sec.Name, err) } } assignMetadata(iter.Ins, raw, &fo, &lo, &ro) raw += iter.Ins.Width() } // Emit the program's instructions. progs[sym.Name] = insns } return progs, nil } // take pops and returns the first item in q if it matches the given predicate // f. Otherwise, it returns nil. func take[T any](q *[]T, f func(T) bool) *T { if q == nil || len(*q) == 0 { return nil } out := (*q)[0] if f(out) { *q = (*q)[1:] return &out } return nil } // Tag the instruction with any ExtInfo metadata that's pointing at the given // raw instruction. func assignMetadata(ins *asm.Instruction, raw asm.RawInstructionOffset, fo *btf.FuncOffsets, lo *btf.LineOffsets, ro *btf.CORERelocationOffsets) { if f := take(fo, func(f btf.FuncOffset) bool { return f.Offset == raw }); f != nil { *ins = btf.WithFuncMetadata(*ins, f.Func) } if l := take(lo, func(l btf.LineOffset) bool { return l.Offset == raw }); l != nil { *ins = ins.WithSource(l.Line) } if r := take(ro, func(r btf.CORERelocationOffset) bool { return r.Offset == raw }); r != nil { *ins = btf.WithCORERelocationMetadata(*ins, r.Relo) } } // referenceRelativeJump turns a relative jump to another bpf subprogram within // the same ELF section into a Reference Instruction. // // Up to LLVM 9, calls to subprograms within the same ELF section are sometimes // encoded using relative jumps instead of relocation entries. These jumps go // out of bounds of the current program, so their targets must be memoized // before the section's instruction stream is split. // // The relative jump Constant is blinded to -1 and the target Symbol is set as // the Instruction's Reference so it can be resolved by the linker. func referenceRelativeJump(ins *asm.Instruction, offset uint64, symbols map[uint64]elf.Symbol) error { if !ins.IsFunctionReference() || ins.Constant == -1 { return nil } tgt := jumpTarget(offset, *ins) sym := symbols[tgt].Name if sym == "" { return fmt.Errorf("no jump target found at offset %d", tgt) } *ins = ins.WithReference(sym) ins.Constant = -1 return nil } // jumpTarget takes ins' offset within an instruction stream (in bytes) // and returns its absolute jump destination (in bytes) within the // instruction stream. func jumpTarget(offset uint64, ins asm.Instruction) uint64 { // A relative jump instruction describes the amount of raw BPF instructions // to jump, convert the offset into bytes. dest := ins.Constant * asm.InstructionSize // The starting point of the jump is the end of the current instruction. dest += int64(offset + asm.InstructionSize) if dest < 0 { return 0 } return uint64(dest) } var errUnsupportedBinding = errors.New("unsupported binding") func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) error { var ( typ = elf.ST_TYPE(rel.Info) bind = elf.ST_BIND(rel.Info) name = rel.Name ) target := ec.sections[rel.Section] switch target.kind { case mapSection, btfMapSection: if bind == elf.STB_LOCAL { return fmt.Errorf("possible erroneous static qualifier on map definition: found reference to %q", name) } if bind != elf.STB_GLOBAL { return fmt.Errorf("map %q: %w: %s", name, errUnsupportedBinding, bind) } if typ != elf.STT_OBJECT && typ != elf.STT_NOTYPE { // STT_NOTYPE is generated on clang < 8 which doesn't tag // relocations appropriately. return fmt.Errorf("map load: incorrect relocation type %v", typ) } ins.Src = asm.PseudoMapFD case dataSection: var offset uint32 switch typ { case elf.STT_SECTION: if bind != elf.STB_LOCAL { return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind) } // This is really a reference to a static symbol, which clang doesn't // emit a symbol table entry for. Instead it encodes the offset in // the instruction itself. offset = uint32(uint64(ins.Constant)) case elf.STT_OBJECT: // LLVM 9 emits OBJECT-LOCAL symbols for anonymous constants. if bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL && bind != elf.STB_WEAK { return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind) } offset = uint32(rel.Value) case elf.STT_NOTYPE: // LLVM 7 emits NOTYPE-LOCAL symbols for anonymous constants. if bind != elf.STB_LOCAL { return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind) } offset = uint32(rel.Value) default: return fmt.Errorf("incorrect relocation type %v for direct map load", typ) } // We rely on using the name of the data section as the reference. It // would be nicer to keep the real name in case of an STT_OBJECT, but // it's not clear how to encode that into Instruction. name = target.Name // The kernel expects the offset in the second basic BPF instruction. ins.Constant = int64(uint64(offset) << 32) ins.Src = asm.PseudoMapValue case programSection: switch opCode := ins.OpCode; { case opCode.JumpOp() == asm.Call: if ins.Src != asm.PseudoCall { return fmt.Errorf("call: %s: incorrect source register", name) } switch typ { case elf.STT_NOTYPE, elf.STT_FUNC: if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK { return fmt.Errorf("call: %s: %w: %s", name, errUnsupportedBinding, bind) } case elf.STT_SECTION: if bind != elf.STB_LOCAL { return fmt.Errorf("call: %s: %w: %s", name, errUnsupportedBinding, bind) } // The function we want to call is in the indicated section, // at the offset encoded in the instruction itself. Reverse // the calculation to find the real function we're looking for. // A value of -1 references the first instruction in the section. offset := int64(int32(ins.Constant)+1) * asm.InstructionSize sym, ok := target.symbols[uint64(offset)] if !ok { return fmt.Errorf("call: no symbol at offset %d", offset) } name = sym.Name ins.Constant = -1 default: return fmt.Errorf("call: %s: invalid symbol type %s", name, typ) } case opCode.IsDWordLoad(): switch typ { case elf.STT_FUNC: if bind != elf.STB_GLOBAL { return fmt.Errorf("load: %s: %w: %s", name, errUnsupportedBinding, bind) } case elf.STT_SECTION: if bind != elf.STB_LOCAL { return fmt.Errorf("load: %s: %w: %s", name, errUnsupportedBinding, bind) } // ins.Constant already contains the offset in bytes from the // start of the section. This is different than a call to a // static function. default: return fmt.Errorf("load: %s: invalid symbol type %s", name, typ) } sym, ok := target.symbols[uint64(ins.Constant)] if !ok { return fmt.Errorf("load: no symbol at offset %d", ins.Constant) } name = sym.Name ins.Constant = -1 ins.Src = asm.PseudoFunc default: return fmt.Errorf("neither a call nor a load instruction: %v", ins) } // The Undefined section is used for 'virtual' symbols that aren't backed by // an ELF section. This includes symbol references from inline asm, forward // function declarations, as well as extern kfunc declarations using __ksym // and extern kconfig variables declared using __kconfig. case undefSection: if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK { return fmt.Errorf("asm relocation: %s: %w: %s", name, errUnsupportedBinding, bind) } if typ != elf.STT_NOTYPE { return fmt.Errorf("asm relocation: %s: unsupported type %s", name, typ) } kf := ec.kfuncs[name] _, ks := ec.ksyms[name] switch { // If a Call / DWordLoad instruction is found and the datasec has a btf.Func with a Name // that matches the symbol name we mark the instruction as a referencing a kfunc. case kf != nil && ins.OpCode.JumpOp() == asm.Call: ins.Metadata.Set(kfuncMetaKey{}, &kfuncMeta{ Func: kf, Binding: bind, }) ins.Src = asm.PseudoKfuncCall ins.Constant = -1 case kf != nil && ins.OpCode.IsDWordLoad(): ins.Metadata.Set(kfuncMetaKey{}, &kfuncMeta{ Func: kf, Binding: bind, }) ins.Constant = 0 case ks && ins.OpCode.IsDWordLoad(): if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK { return fmt.Errorf("asm relocation: %s: %w: %s", name, errUnsupportedBinding, bind) } ins.Metadata.Set(ksymMetaKey{}, &ksymMeta{ Binding: bind, Name: name, }) // If no kconfig map is found, this must be a symbol reference from inline // asm (see testdata/loader.c:asm_relocation()) or a call to a forward // function declaration (see testdata/fwd_decl.c). Don't interfere, These // remain standard symbol references. // extern __kconfig reads are represented as dword loads that need to be // rewritten to pseudo map loads from .kconfig. If the map is present, // require it to contain the symbol to disambiguate between inline asm // relos and kconfigs. case ec.kconfig != nil && ins.OpCode.IsDWordLoad(): if bind != elf.STB_GLOBAL { return fmt.Errorf("asm relocation: %s: %w: %s", name, errUnsupportedBinding, bind) } for _, vsi := range ec.kconfig.Value.(*btf.Datasec).Vars { if vsi.Type.(*btf.Var).Name != rel.Name { continue } ins.Src = asm.PseudoMapValue ins.Metadata.Set(kconfigMetaKey{}, &kconfigMeta{ec.kconfig, vsi.Offset}) return nil } return fmt.Errorf("kconfig %s not found in .kconfig", rel.Name) } default: return fmt.Errorf("relocation to %q: %w", target.Name, ErrNotSupported) } *ins = ins.WithReference(name) return nil } // loadMaps iterates over all ELF sections marked as map sections (like .maps) // and parses each symbol into a MapSpec. func (ec *elfCode) loadMaps() error { for _, sec := range ec.sections { if sec.kind != mapSection { continue } if len(sec.symbols) == 0 { return fmt.Errorf("section %v: no symbols", sec.Name) } vars, err := ec.sectionVars(ec.btf, sec.Name) if err != nil { return fmt.Errorf("section %v: loading map variable BTF: %w", sec.Name, err) } for _, sym := range sec.symbols { name := sym.Name if ec.maps[name] != nil { return fmt.Errorf("duplicate symbol %s in section %s", name, sec.Name) } sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size)) spec := MapSpec{ Name: sanitizeName(name, -1), } switch { case binary.Read(sr, ec.ByteOrder, &spec.Type) != nil: return fmt.Errorf("map %s: missing type", name) case binary.Read(sr, ec.ByteOrder, &spec.KeySize) != nil: return fmt.Errorf("map %s: missing key size", name) case binary.Read(sr, ec.ByteOrder, &spec.ValueSize) != nil: return fmt.Errorf("map %s: missing value size", name) case binary.Read(sr, ec.ByteOrder, &spec.MaxEntries) != nil: return fmt.Errorf("map %s: missing max entries", name) case binary.Read(sr, ec.ByteOrder, &spec.Flags) != nil: return fmt.Errorf("map %s: missing flags", name) } extra, err := io.ReadAll(sr) if err != nil { return fmt.Errorf("map %s: reading map tail: %w", name, err) } if len(extra) > 0 { spec.Extra = bytes.NewReader(extra) } if v, ok := vars[name]; ok { spec.Tags = slices.Clone(v.Tags) } ec.maps[name] = &spec } } return nil } // sectionVars looks up the BTF Datasec for the given section name and returns a // map of variable names to their btf.Var definitions. func (ec *elfCode) sectionVars(spec *btf.Spec, sec string) (map[string]*btf.Var, error) { vars := make(map[string]*btf.Var) if spec == nil { return vars, nil } var ds *btf.Datasec if err := ec.btf.TypeByName(sec, &ds); err != nil { return vars, nil } for _, vsi := range ds.Vars { v, ok := btf.As[*btf.Var](vsi.Type) if !ok { return nil, fmt.Errorf("btf.VarSecInfo doesn't point to a *btf.Var: %T", vsi.Type) } vars[string(v.Name)] = v } return vars, nil } // loadBTFMaps iterates over all ELF sections marked as BTF map sections // (like .maps) and parses them into MapSpecs. Dump the .maps section and // any relocations with `readelf -x .maps -r `. func (ec *elfCode) loadBTFMaps() error { for _, sec := range ec.sections { if sec.kind != btfMapSection { continue } if ec.btf == nil { return fmt.Errorf("missing BTF") } vars, err := ec.sectionVars(ec.btf, sec.Name) if err != nil { return fmt.Errorf("section %v: loading map variable BTF: %w", sec.Name, err) } if len(vars) != len(sec.symbols) { return fmt.Errorf("section %v: contains %d symbols but %d btf.Vars", sec.Name, len(sec.symbols), len(vars)) } syms := make(map[string]elf.Symbol) for _, sym := range sec.symbols { syms[sym.Name] = sym } for _, v := range vars { name := v.Name // Find the ELF symbol corresponding to this Var. sym, ok := syms[name] if !ok { return fmt.Errorf("section %v: missing symbol for map %s", sec.Name, name) } sr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size)) // The BTF metadata for each Var contains the full length of the map // declaration, so read the corresponding amount of bytes from the ELF. // This way, we can pinpoint which map declaration contains unexpected // (and therefore unsupported) data. if _, err = io.Copy(internal.DiscardZeroes{}, sr); err != nil { return fmt.Errorf("section %v: map %s: initializing BTF map definitions: %w", sec.Name, name, internal.ErrNotSupported) } if ec.maps[name] != nil { return fmt.Errorf("section %v: map %s already exists", sec.Name, name) } // Each Var representing a BTF map definition contains a Struct. mapStruct, ok := btf.UnderlyingType(v.Type).(*btf.Struct) if !ok { return fmt.Errorf("expected struct, got %s", v.Type) } spec, err := mapSpecFromBTF(sec, sym, v, mapStruct, ec.btf, name, false) if err != nil { return fmt.Errorf("map %v: %w", name, err) } ec.maps[name] = spec } } return nil } // mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing // a BTF map definition. The name and spec arguments will be copied to the // resulting MapSpec, and inner must be true on any recursive invocations. func mapSpecFromBTF(es *elfSection, sym elf.Symbol, v *btf.Var, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) { var ( key, value btf.Type keySize, valueSize uint64 mapType MapType flags, maxEntries uint64 pinType PinType mapExtra uint64 innerMapSpec *MapSpec contents []MapKV err error ) for i, member := range def.Members { switch member.Name { case "type": mt, err := uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get type: %w", err) } mapType = MapType(mt) case "map_flags": flags, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF map flags: %w", err) } case "max_entries": maxEntries, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF map max entries: %w", err) } case "key": if keySize != 0 { return nil, errors.New("both key and key_size given") } pk, ok := member.Type.(*btf.Pointer) if !ok { return nil, fmt.Errorf("key type is not a pointer: %T", member.Type) } key = pk.Target size, err := btf.Sizeof(pk.Target) if err != nil { return nil, fmt.Errorf("can't get size of BTF key: %w", err) } keySize = uint64(size) case "value": if valueSize != 0 { return nil, errors.New("both value and value_size given") } vk, ok := member.Type.(*btf.Pointer) if !ok { return nil, fmt.Errorf("value type is not a pointer: %T", member.Type) } value = vk.Target size, err := btf.Sizeof(vk.Target) if err != nil { return nil, fmt.Errorf("can't get size of BTF value: %w", err) } valueSize = uint64(size) case "key_size": // Key needs to be nil and keySize needs to be 0 for key_size to be // considered a valid member. if key != nil || keySize != 0 { return nil, errors.New("both key and key_size given") } keySize, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF key size: %w", err) } case "value_size": // Value needs to be nil and valueSize needs to be 0 for value_size to be // considered a valid member. if value != nil || valueSize != 0 { return nil, errors.New("both value and value_size given") } valueSize, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get BTF value size: %w", err) } case "pinning": if inner { return nil, errors.New("inner maps can't be pinned") } pinning, err := uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get pinning: %w", err) } pinType = PinType(pinning) case "values": // The 'values' field in BTF map definitions is used for declaring map // value types that are references to other BPF objects, like other maps // or programs. It is always expected to be an array of pointers. if i != len(def.Members)-1 { return nil, errors.New("'values' must be the last member in a BTF map definition") } if valueSize != 0 && valueSize != 4 { return nil, errors.New("value_size must be 0 or 4") } valueSize = 4 valueType, err := resolveBTFArrayMacro(member.Type) if err != nil { return nil, fmt.Errorf("can't resolve type of member 'values': %w", err) } switch t := valueType.(type) { case *btf.Struct: // The values member pointing to an array of structs means we're expecting // a map-in-map declaration. if mapType != ArrayOfMaps && mapType != HashOfMaps { return nil, errors.New("outer map needs to be an array or a hash of maps") } if inner { return nil, fmt.Errorf("nested inner maps are not supported") } // This inner map spec is used as a map template, but it needs to be // created as a traditional map before it can be used to do so. // libbpf names the inner map template '.inner', but we // opted for _inner to simplify validation logic. (dots only supported // on kernels 5.2 and up) // Pass the BTF spec from the parent object, since both parent and // child must be created from the same BTF blob (on kernels that support BTF). innerMapSpec, err = mapSpecFromBTF(es, sym, v, t, spec, name+"_inner", true) if err != nil { return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err) } case *btf.FuncProto: // The values member contains an array of function pointers, meaning an // autopopulated PROG_ARRAY. if mapType != ProgramArray { return nil, errors.New("map needs to be a program array") } default: return nil, fmt.Errorf("unsupported value type %q in 'values' field", t) } contents, err = resolveBTFValuesContents(es, sym, member) if err != nil { return nil, fmt.Errorf("resolving values contents: %w", err) } case "map_extra": mapExtra, err = uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("resolving map_extra: %w", err) } default: return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name) } } // Some maps don't support value sizes, but annotating their map definitions // with __type macros can still be useful, especially to let bpf2go generate // type definitions for them. if value != nil && !mapType.canHaveValueSize() { valueSize = 0 } return &MapSpec{ Name: sanitizeName(name, -1), Type: MapType(mapType), KeySize: uint32(keySize), ValueSize: uint32(valueSize), MaxEntries: uint32(maxEntries), Flags: uint32(flags), Key: key, Value: value, Pinning: pinType, InnerMap: innerMapSpec, Contents: contents, Tags: slices.Clone(v.Tags), MapExtra: mapExtra, }, nil } // uintFromBTF resolves the __uint and __ulong macros. // // __uint emits a pointer to a sized array. For int (*foo)[10], this function // will return 10. // // __ulong emits an enum with a single value that can represent a 64-bit // integer. The first (and only) enum value is returned. func uintFromBTF(typ btf.Type) (uint64, error) { switch t := typ.(type) { case *btf.Pointer: arr, ok := t.Target.(*btf.Array) if !ok { return 0, fmt.Errorf("not a pointer to array: %v", typ) } return uint64(arr.Nelems), nil case *btf.Enum: if len(t.Values) == 0 { return 0, errors.New("enum has no values") } return t.Values[0].Value, nil default: return 0, fmt.Errorf("not a pointer or enum: %v", typ) } } // resolveBTFArrayMacro resolves the __array macro, which declares an array // of pointers to a given type. This function returns the target Type of // the pointers in the array. func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) { arr, ok := typ.(*btf.Array) if !ok { return nil, fmt.Errorf("not an array: %v", typ) } ptr, ok := arr.Type.(*btf.Pointer) if !ok { return nil, fmt.Errorf("not an array of pointers: %v", typ) } return ptr.Target, nil } // valuesRelocations returns an iterator over the relocations in the ELF section // corresponding to the elements of a .values array in a BTF map definition. Each // iteration yields the array index and the symbol referenced by the relocation // at that index. Empty indices are skipped. func valuesRelocations(es *elfSection, sym elf.Symbol, member btf.Member) iter.Seq2[uint32, elf.Symbol] { // The elements of a .values pointer array are not encoded in BTF itself. // Instead, each array index receives a relocation pointing at a symbol // (map/prog) in another section. However, it's possible to leave certain // array indices empty, so all indices' offsets need to be checked for emitted // relocations. // Absolute offset of the .values member within the section. start := sym.Value + uint64(member.Offset.Bytes()) // .values is a variable-length struct member, so its contents run until the // end of the symbol. The symbol offset + size is the absolute offset of the // end of the array in the section. end := sym.Value + sym.Size // The size of an address in this section. This determines the width of an // index in the array. align := es.Addralign // Amount of elements in the .values array. elems := (end - start) / align return func(yield func(uint32, elf.Symbol) bool) { for i := range uint32(elems) { // off increases by align on each iteration, starting at .values. off := start + (uint64(i) * align) r, ok := es.relocations[off] if !ok { continue } if !yield(i, r) { return } } } } // resolveBTFValuesContents looks up the symbols referenced by the relocations // in a .values array and returns them as MapKV pairs, where the key is the // array index and the value is the symbol name. Empty indices are skipped. func resolveBTFValuesContents(es *elfSection, sym elf.Symbol, member btf.Member) ([]MapKV, error) { var contents []MapKV if member.Offset.Bytes() > uint32(sym.Size) { return nil, fmt.Errorf("member offset %d exceeds symbol size %d", member.Offset.Bytes(), sym.Size) } for i, sym := range valuesRelocations(es, sym, member) { // Emit a value stub based on the type of relocation to be replaced by a // real fd later in the pipeline before populating the Map. switch t := elf.ST_TYPE(sym.Info); t { case elf.STT_FUNC: contents = append(contents, MapKV{i, sym.Name}) case elf.STT_OBJECT: contents = append(contents, MapKV{i, sym.Name}) default: return nil, fmt.Errorf("unknown relocation type %v for symbol %s", t, sym.Name) } } return contents, nil } func (ec *elfCode) loadDataSections() error { for _, sec := range ec.sections { if sec.kind != dataSection { continue } // If a section has no references, it will be freed as soon as the // Collection closes, so creating and populating it is wasteful. If it has // no symbols, it is likely an ephemeral section used during compilation // that wasn't sanitized by the bpf linker. (like .rodata.str1.1) // // No symbols means no VariableSpecs can be generated from it, making it // pointless to emit a data section for. if sec.references == 0 && len(sec.symbols) == 0 { continue } if sec.Size > math.MaxUint32 { return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name) } mapSpec := &MapSpec{ Name: sanitizeName(sec.Name, -1), Type: Array, KeySize: 4, ValueSize: uint32(sec.Size), MaxEntries: 1, } if isConstantDataSection(sec.Name) { mapSpec.Flags = sys.BPF_F_RDONLY_PROG } var data []byte switch sec.Type { // Only open the section if we know there's actual data to be read. case elf.SHT_PROGBITS: var err error data, err = sec.Data() if err != nil { return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err) } case elf.SHT_NOBITS: // NOBITS sections like .bss contain only zeroes and are not allocated in // the ELF. Since data sections are Arrays, the kernel can preallocate // them. Don't attempt reading zeroes from the ELF, instead allocate the // zeroed memory to support getting and setting VariableSpecs for sections // like .bss. data = make([]byte, sec.Size) default: return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type) } mapSpec.Contents = []MapKV{{uint32(0), data}} for off, sym := range sec.symbols { // Skip symbols marked with the 'hidden' attribute. if elf.ST_VISIBILITY(sym.Other) == elf.STV_HIDDEN || elf.ST_VISIBILITY(sym.Other) == elf.STV_INTERNAL { continue } // Only accept symbols with global or weak bindings. The common // alternative is STB_LOCAL, which are either function-scoped or declared // 'static'. if elf.ST_BIND(sym.Info) != elf.STB_GLOBAL && elf.ST_BIND(sym.Info) != elf.STB_WEAK { continue } if ec.vars[sym.Name] != nil { return fmt.Errorf("data section %s: duplicate variable %s", sec.Name, sym.Name) } // Skip symbols starting with a dot, they are compiler-internal symbols // emitted by clang 11 and earlier and are not cleaned up by the bpf // compiler backend (e.g. symbols named .Lconstinit.1 in sections like // .rodata.cst32). Variables in C cannot start with a dot, so filter these // out. if strings.HasPrefix(sym.Name, ".") { continue } if off+sym.Size > uint64(len(data)) { return fmt.Errorf("data section %s: variable %s exceeds section bounds", sec.Name, sym.Name) } if off > math.MaxUint32 { return fmt.Errorf("data section %s: variable %s offset %d exceeds maximum", sec.Name, sym.Name, off) } ec.vars[sym.Name] = &VariableSpec{ SectionName: sec.Name, Name: sym.Name, Offset: uint32(off), Value: slices.Clone(data[off : off+sym.Size]), } } // It is possible for a data section to exist without a corresponding BTF Datasec // if it only contains anonymous values like macro-defined arrays. if ec.btf != nil { var ds *btf.Datasec if ec.btf.TypeByName(sec.Name, &ds) == nil { // Assign the spec's key and BTF only if the Datasec lookup was successful. mapSpec.Key = &btf.Void{} mapSpec.Value = ds // Populate VariableSpecs with type information, if available. for _, v := range ds.Vars { name := v.Type.TypeName() if name == "" { return fmt.Errorf("data section %s: anonymous variable %v", sec.Name, v) } vt, ok := v.Type.(*btf.Var) if !ok { return fmt.Errorf("data section %s: unexpected type %T for variable %s", sec.Name, v.Type, name) } ev := ec.vars[name] if ev == nil { // Hidden symbols appear in the BTF Datasec but don't receive a VariableSpec. continue } if v.Offset != ev.Offset { return fmt.Errorf("data section %s: variable %s datasec offset (%d) doesn't match ELF symbol offset (%d)", sec.Name, name, v.Offset, ev.Offset) } if v.Size != ev.Size() { return 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()) } // Decouple the Var in the VariableSpec from the underlying DataSec in // the MapSpec to avoid modifications from affecting map loads later on. ev.Type = btf.Copy(vt).(*btf.Var) } } } ec.maps[sec.Name] = mapSpec } return nil } // loadKconfigSection handles the 'virtual' Datasec .kconfig that doesn't // have a corresponding ELF section and exist purely in BTF. func (ec *elfCode) loadKconfigSection() error { if ec.btf == nil { return nil } var ds *btf.Datasec err := ec.btf.TypeByName(".kconfig", &ds) if errors.Is(err, btf.ErrNotFound) { return nil } if err != nil { return err } if ds.Size == 0 { return errors.New("zero-length .kconfig") } ec.kconfig = &MapSpec{ Name: ".kconfig", Type: Array, KeySize: uint32(4), ValueSize: ds.Size, MaxEntries: 1, Flags: sys.BPF_F_RDONLY_PROG, Key: &btf.Int{Size: 4}, Value: ds, } return nil } // loadKsymsSection handles the 'virtual' Datasec .ksyms that doesn't // have a corresponding ELF section and exist purely in BTF. func (ec *elfCode) loadKsymsSection() error { if ec.btf == nil { return nil } var ds *btf.Datasec err := ec.btf.TypeByName(".ksyms", &ds) if errors.Is(err, btf.ErrNotFound) { return nil } if err != nil { return err } for _, v := range ds.Vars { switch t := v.Type.(type) { case *btf.Func: ec.kfuncs[t.TypeName()] = t case *btf.Var: ec.ksyms[t.TypeName()] = struct{}{} default: return fmt.Errorf("unexpected variable type in .ksyms: %T", v) } } return nil } // associateStructOpsRelocs handles `.struct_ops.link` // and associates the target function with the correct struct member in the map. func (ec *elfCode) associateStructOpsRelocs(progs map[string]*ProgramSpec) error { for _, sec := range ec.sections { if sec.kind != structOpsSection { continue } userData, err := sec.Data() if err != nil { return fmt.Errorf("failed to read section data: %w", err) } // Resolve the BTF datasec describing variables in this section. var ds *btf.Datasec if err := ec.btf.TypeByName(sec.Name, &ds); err != nil { return fmt.Errorf("datasec %s: %w", sec.Name, err) } // Set flags for .struct_ops.link (BPF_F_LINK). flags := uint32(0) if sec.Name == structOpsLinkSec { flags = sys.BPF_F_LINK } for _, vsi := range ds.Vars { userSt, baseOff, err := ec.createStructOpsMap(vsi, userData, flags) if err != nil { return err } if err := structOpsSetAttachTo(sec, baseOff, userSt, progs); err != nil { return err } } } return nil } // createStructOpsMap() creates and registers a MapSpec for a struct_ops func (ec *elfCode) createStructOpsMap(vsi btf.VarSecinfo, userData []byte, flags uint32) (*btf.Struct, uint32, error) { varType, ok := btf.As[*btf.Var](vsi.Type) if !ok { return nil, 0, fmt.Errorf("vsi: expect var, got %T", vsi.Type) } mapName := varType.Name userSt, ok := btf.As[*btf.Struct](varType.Type) if !ok { return nil, 0, fmt.Errorf("var %s: expect struct, got %T", varType.Name, varType.Type) } userSize := userSt.Size baseOff := vsi.Offset if baseOff+userSize > uint32(len(userData)) { return nil, 0, fmt.Errorf("%s exceeds section", mapName) } // Register the MapSpec for this struct_ops instance if doesn't exist if _, exists := ec.maps[mapName]; exists { return nil, 0, fmt.Errorf("struct_ops map %s: already exists", mapName) } ec.maps[mapName] = &MapSpec{ Name: mapName, Type: StructOpsMap, Key: &btf.Int{Size: 4}, KeySize: structOpsKeySize, ValueSize: userSize, // length of the user-struct type Value: userSt, Flags: flags, MaxEntries: 1, Contents: []MapKV{ { Key: uint32(0), Value: append([]byte(nil), userData[baseOff:baseOff+userSize]...), }, }, } return userSt, baseOff, nil } type libbpfElfSectionDef struct { pattern string programType sys.ProgType attachType sys.AttachType flags libbpfElfSectionFlag } type libbpfElfSectionFlag uint32 // The values correspond to enum sec_def_flags in libbpf. const ( _SEC_NONE libbpfElfSectionFlag = 0 _SEC_EXP_ATTACH_OPT libbpfElfSectionFlag = 1 << (iota - 1) _SEC_ATTACHABLE _SEC_ATTACH_BTF _SEC_SLEEPABLE _SEC_XDP_FRAGS _SEC_USDT _SEC_ATTACHABLE_OPT = _SEC_ATTACHABLE | _SEC_EXP_ATTACH_OPT ) func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { // Skip optional program marking for now. sectionName = strings.TrimPrefix(sectionName, "?") for _, t := range elfSectionDefs { extra, ok := matchSectionName(sectionName, t.pattern) if !ok { continue } programType := ProgramType(t.programType) attachType := AttachType(t.attachType) var flags uint32 if t.flags&_SEC_SLEEPABLE > 0 { flags |= sys.BPF_F_SLEEPABLE } if t.flags&_SEC_XDP_FRAGS > 0 { flags |= sys.BPF_F_XDP_HAS_FRAGS } // The libbpf documentation on program types states: 'The struct_ops attach // format supports struct_ops[.s]/ convention, but name is ignored and // it is recommended to just use plain SEC("struct_ops[.s]").' // // Ignore any extra for struct_ops to match libbpf behaviour. if programType == StructOps { extra = "" } return programType, attachType, flags, extra } return UnspecifiedProgram, AttachNone, 0, "" } // matchSectionName checks a section name against a pattern. // // It's behaviour mirrors that of libbpf's sec_def_matches. func matchSectionName(sectionName, pattern string) (extra string, found bool) { have, extra, found := strings.Cut(sectionName, "/") want := strings.TrimRight(pattern, "+/") if strings.HasSuffix(pattern, "/") { // Section name must have a slash and extra may be empty. return extra, have == want && found } else if strings.HasSuffix(pattern, "+") { // Section name may have a slash and extra may be empty. return extra, have == want } // Section name must have a prefix. extra is ignored. return "", strings.HasPrefix(sectionName, pattern) } func (ec *elfCode) loadSectionRelocations(sec *elf.Section, symbols []elf.Symbol) (map[uint64]elf.Symbol, error) { rels := make(map[uint64]elf.Symbol) if sec.Entsize < 16 { return nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name) } r := bufio.NewReader(sec.Open()) for off := uint64(0); off < sec.Size; off += sec.Entsize { ent := io.LimitReader(r, int64(sec.Entsize)) var rel elf.Rel64 if binary.Read(ent, ec.ByteOrder, &rel) != nil { return nil, fmt.Errorf("can't parse relocation at offset %v", off) } symNo := int(elf.R_SYM64(rel.Info) - 1) if symNo >= len(symbols) { return nil, fmt.Errorf("offset %d: symbol %d doesn't exist", off, symNo) } symbol := symbols[symNo] rels[rel.Off] = symbol } return rels, nil } golang-github-cilium-ebpf-0.21.0+ds1/elf_reader_test.go000066400000000000000000001146361520243672000226700ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "flag" "fmt" "maps" "os" "path/filepath" "strings" "syscall" "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/kallsyms" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/go-quicktest/qt" ) var csCmpOpts = cmp.Options{ // Dummy Comparer that works with empty readers to support test cases. cmp.Comparer(func(a, b bytes.Reader) bool { if a.Len() == 0 && b.Len() == 0 { return true } return false }), cmpopts.IgnoreTypes(btf.Spec{}), cmpopts.IgnoreFields(CollectionSpec{}, "ByteOrder", "Types"), cmpopts.IgnoreFields(ProgramSpec{}, "Instructions", "ByteOrder"), cmpopts.IgnoreFields(MapSpec{}, "Key", "Value", "Contents"), cmpopts.IgnoreFields(VariableSpec{}, "Type", "Value"), cmpopts.IgnoreUnexported(ProgramSpec{}), } func TestLoadCollectionSpec(t *testing.T) { coll := &CollectionSpec{ Maps: map[string]*MapSpec{ "hash_map": { Name: "hash_map", Type: Hash, KeySize: 4, ValueSize: 8, MaxEntries: 1, Flags: sys.BPF_F_NO_PREALLOC, }, "hash_map2": { Name: "hash_map2", Type: Hash, KeySize: 4, ValueSize: 8, MaxEntries: 2, }, "perf_event_array": { Name: "perf_event_array", Type: PerfEventArray, MaxEntries: 4096, }, ".bss": { Name: ".bss", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, ".data": { Name: ".data", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, ".data.test": { Name: ".data.test", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, ".rodata": { Name: ".rodata", Type: Array, KeySize: 4, ValueSize: 24, MaxEntries: 1, Flags: sys.BPF_F_RDONLY_PROG, }, ".rodata.test": { Name: ".rodata.test", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, Flags: sys.BPF_F_RDONLY_PROG, }, ".rodata.cst32": { Name: ".rodata.cst32", Type: Array, KeySize: 4, ValueSize: 32, MaxEntries: 1, Flags: sys.BPF_F_RDONLY_PROG, }, }, Programs: map[string]*ProgramSpec{ "xdp_prog": { Name: "xdp_prog", Type: XDP, SectionName: "xdp", AttachType: AttachXDP, License: "MIT", }, "no_relocation": { Name: "no_relocation", Type: SocketFilter, SectionName: "socket", License: "MIT", }, "asm_relocation": { Name: "asm_relocation", Type: SocketFilter, SectionName: "socket/2", License: "MIT", }, "data_sections": { Name: "data_sections", Type: SocketFilter, SectionName: "socket/3", License: "MIT", }, "global_fn3": { Name: "global_fn3", Type: UnspecifiedProgram, SectionName: "other", License: "MIT", }, "static_fn": { Name: "static_fn", Type: UnspecifiedProgram, SectionName: "static", License: "MIT", }, "anon_const": { Name: "anon_const", Type: SocketFilter, SectionName: "socket/4", License: "MIT", }, }, Variables: map[string]*VariableSpec{ "arg": {Name: "arg", SectionName: ".rodata", Offset: 4}, "arg2": {Name: "arg2", SectionName: ".rodata.test", Offset: 0}, "arg3": {Name: "arg3", SectionName: ".data.test", Offset: 0}, "key1": {Name: "key1", SectionName: ".bss", Offset: 0}, "key2": {Name: "key2", SectionName: ".data", Offset: 0}, "key3": {Name: "key3", SectionName: ".rodata", Offset: 0}, "neg": {Name: "neg", SectionName: ".rodata", Offset: 12}, "uneg": {Name: "uneg", SectionName: ".rodata", Offset: 8}, }, } // BTF-only maps. btfOnly := map[string]*MapSpec{ "btf_pin": { Name: "btf_pin", Type: Hash, KeySize: 4, ValueSize: 8, MaxEntries: 1, Pinning: PinByName, }, "bpf_decl_map": { Name: "bpf_decl_map", Type: Array, KeySize: 4, ValueSize: 8, MaxEntries: 1, Tags: []string{"a", "b"}, }, "btf_decl_map": { Name: "btf_decl_map", Type: Array, KeySize: 4, ValueSize: 8, MaxEntries: 1, Tags: []string{"a", "b"}, }, "btf_outer_map": { Name: "btf_outer_map", Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 1, InnerMap: &MapSpec{ Name: "btf_outer_map_inner", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, "btf_outer_map_anon": { Name: "btf_outer_map_anon", Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 1, InnerMap: &MapSpec{ Name: "btf_outer_map_anon_inner", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, }, "btf_typedef_map": { Name: "btf_typedef_map", Type: Array, KeySize: 4, ValueSize: 8, MaxEntries: 1, }, } testutils.Files(t, testutils.Glob(t, "testdata/loader-*.elf"), func(t *testing.T, file string) { got, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) // BTF map definition contains a value type, but the size should remain 0. // The value type needs to be reflected in the MapSpec. qt.Assert(t, qt.Equals(got.Maps["perf_event_array"].ValueSize, 0)) qt.Assert(t, qt.IsNotNil(got.Maps["perf_event_array"].Value)) // Copy and extend the CollectionSpec with BTF-only objects. want := coll.Copy() maps.Copy(want.Maps, btfOnly) testLoadCollectionSpec(t, got, want) }) testutils.Files(t, testutils.Glob(t, "testdata/loader_nobtf-*.elf"), func(t *testing.T, file string) { got, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) testLoadCollectionSpec(t, got, coll.Copy()) }) } func testLoadCollectionSpec(t *testing.T, got, want *CollectionSpec) { t.Helper() qt.Assert(t, qt.CmpEquals(got, want, csCmpOpts)) coll, err := newCollection(t, got, &CollectionOptions{ Maps: MapOptions{PinPath: testutils.TempBPFFS(t)}, Programs: ProgramOptions{LogLevel: LogLevelBranch}, }) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) ret := mustRun(t, coll.Programs["xdp_prog"], nil) qt.Assert(t, qt.Equals(ret, 7)) } func BenchmarkELFLoader(b *testing.B) { b.ReportAllocs() for b.Loop() { _, _ = LoadCollectionSpec("testdata/loader-el.elf") } } func TestDataSections(t *testing.T) { file := testutils.NativeFile(t, "testdata/loader-%s.elf") coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } t.Log(coll.Programs["data_sections"].Instructions) var obj struct { Program *Program `ebpf:"data_sections"` } mustLoadAndAssign(t, coll, &obj, nil) defer obj.Program.Close() ret := mustRun(t, obj.Program, nil) if ret != 0 { t.Error("BPF assertion failed on line", ret) } } func TestInlineASMConstant(t *testing.T) { file := testutils.NativeFile(t, "testdata/loader-%s.elf") coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } spec := coll.Programs["asm_relocation"] if spec.Instructions[0].Reference() != "MY_CONST" { t.Fatal("First instruction is not a reference to MY_CONST") } // -1 is used by the loader to find unrewritten maps. spec.Instructions[0].Constant = -1 t.Log(spec.Instructions) var obj struct { Program *Program `ebpf:"asm_relocation"` } mustLoadAndAssign(t, coll, &obj, nil) obj.Program.Close() } func TestFreezeRodata(t *testing.T) { testutils.SkipOnOldKernel(t, "5.9", "sk_lookup program type") file := testutils.NativeFile(t, "testdata/constants-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var obj struct { Program *Program `ebpf:"freeze_rodata"` } qt.Assert(t, qt.IsNil(spec.Variables["ret"].Set(uint32(1)))) mustLoadAndAssign(t, spec, &obj, nil) obj.Program.Close() } func TestCollectionSpecDetach(t *testing.T) { coll := Collection{ Maps: map[string]*Map{ "foo": new(Map), }, Programs: map[string]*Program{ "bar": new(Program), }, } foo := coll.DetachMap("foo") if foo == nil { t.Error("Program not returned from DetachMap") } if _, ok := coll.Programs["foo"]; ok { t.Error("DetachMap doesn't remove map from Maps") } bar := coll.DetachProgram("bar") if bar == nil { t.Fatal("Program not returned from DetachProgram") } if _, ok := coll.Programs["bar"]; ok { t.Error("DetachProgram doesn't remove program from Programs") } } func TestLoadInvalidMap(t *testing.T) { file := testutils.NativeFile(t, "testdata/invalid_map-%s.elf") cs, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't load CollectionSpec", err) } ms, ok := cs.Maps["invalid_map"] if !ok { t.Fatal("invalid_map not found in CollectionSpec") } m, err := NewMap(ms) t.Log(err) if err == nil { m.Close() t.Fatal("Creating a Map from a MapSpec with non-zero Extra is expected to fail.") } } func TestLoadInvalidMapMissingSymbol(t *testing.T) { file := testutils.NativeFile(t, "testdata/invalid_map_static-%s.elf") _, err := LoadCollectionSpec(file) t.Log(err) if err == nil { t.Fatal("Loading a map with static qualifier should fail") } } func TestLoadInitializedBTFMap(t *testing.T) { testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) { coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } t.Run("NewCollection", func(t *testing.T) { _, err := newCollection(t, coll, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("NewCollection failed:", err) } }) t.Run("prog_array", func(t *testing.T) { m, ok := coll.Maps["prog_array_init"] if !ok { t.Fatal("map prog_array_init not found in program") } if len(m.Contents) != 1 { t.Error("expecting exactly 1 item in MapSpec contents") } p := m.Contents[0] if cmp.Equal(p.Key, 1) { t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key) } if _, ok := p.Value.(string); !ok { t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value) } if p.Value != "tail_1" { t.Errorf("expected MapSpec entry Value 'tail_1', got: %s", p.Value) } }) t.Run("array_of_maps", func(t *testing.T) { m, ok := coll.Maps["outer_map_init"] if !ok { t.Fatal("map outer_map_init not found in program") } if len(m.Contents) != 1 { t.Error("expecting exactly 1 item in MapSpec contents") } if m.Key == nil { t.Error("Expected non-nil key") } if m.Value == nil { t.Error("Expected non-nil value") } if m.InnerMap.Key == nil { t.Error("Expected non-nil InnerMap key") } if m.InnerMap.Value == nil { t.Error("Expected non-nil InnerMap value") } p := m.Contents[0] if cmp.Equal(p.Key, 1) { t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key) } if _, ok := p.Value.(string); !ok { t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value) } if p.Value != "inner_map" { t.Errorf("expected MapSpec entry Value 'inner_map', got: %s", p.Value) } }) }) } func TestLoadInvalidInitializedBTFMap(t *testing.T) { file := testutils.NativeFile(t, "testdata/invalid_btf_map_init-%s.elf") _, err := LoadCollectionSpec(file) t.Log(err) if !errors.Is(err, internal.ErrNotSupported) { t.Fatal("Loading an initialized BTF map should be unsupported") } } func TestStringSection(t *testing.T) { file := testutils.NativeFile(t, "testdata/strings-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatalf("load collection spec: %s", err) } for name := range spec.Maps { t.Log(name) } strMap := spec.Maps[".rodata.str1.1"] if strMap == nil { t.Fatal("Unable to find map '.rodata.str1.1' in loaded collection") } if !strMap.readOnly() { t.Fatal("Read only data maps should be frozen") } if strMap.Flags != sys.BPF_F_RDONLY_PROG { t.Fatal("Read only data maps should have the prog-read-only flag set") } coll, err := newCollection(t, spec, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("new collection: %s", err) } prog := coll.Programs["filter"] if prog == nil { t.Fatal("program not found") } testMap := coll.Maps["my_map"] if testMap == nil { t.Fatal("test map not found") } _, err = prog.Run(&RunOptions{ Data: internal.EmptyBPFContext, // Min size for XDP programs }) if err != nil { t.Fatalf("prog run: %s", err) } key := []byte("This string is allocated in the string section\n\x00") var value uint32 if err = testMap.Lookup(&key, &value); err != nil { t.Fatalf("test map lookup: %s", err) } if value != 1 { t.Fatal("Test map value not 1!") } } func TestLoadRawTracepoint(t *testing.T) { testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API") file := testutils.NativeFile(t, "testdata/raw_tracepoint-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } coll, err := NewCollectionWithOptions(spec, CollectionOptions{ Programs: ProgramOptions{ LogLevel: LogLevelBranch, }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create collection:", err) } coll.Close() } func TestTailCall(t *testing.T) { file := testutils.NativeFile(t, "testdata/btf_map_init-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var obj struct { TailMain *Program `ebpf:"tail_main"` ProgArray *Map `ebpf:"prog_array_init"` // Windows evicts programs from the tail call array when the last // user space reference is closed. This is not the case on Linux. Tail *Program `ebpf:"tail_1"` } err = loadAndAssign(t, spec, &obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.TailMain.Close() defer obj.Tail.Close() defer obj.ProgArray.Close() ret := mustRun(t, obj.Tail, nil) // Expect the tail_1 tail call to be taken, returning value 42. if ret != 42 { t.Fatalf("Expected tail call to return value 42, got %d", ret) } } func TestKconfig(t *testing.T) { file := testutils.NativeFile(t, "testdata/kconfig-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var obj struct { Main *Program `ebpf:"kconfig"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Main.Close() ret := mustRun(t, obj.Main, nil) qt.Assert(t, qt.Equals(ret, 0), qt.Commentf("Failed assertion at line %d in testdata/kconfig.c", ret)) } func TestKsym(t *testing.T) { file := testutils.NativeFile(t, "testdata/ksym-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) var obj struct { Main *Program `ebpf:"ksym_test"` ArrayMap *Map `ebpf:"array_map"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) defer obj.Main.Close() defer obj.ArrayMap.Close() mustRun(t, obj.Main, nil) ksyms := map[string]uint64{ "bpf_init": 0, "bpf_trace_run1": 0, } qt.Assert(t, qt.IsNil(kallsyms.AssignAddresses(ksyms))) qt.Assert(t, qt.Not(qt.Equals(ksyms["bpf_init"], 0))) qt.Assert(t, qt.Not(qt.Equals(ksyms["bpf_trace_run1"], 0))) var value uint64 qt.Assert(t, qt.IsNil(obj.ArrayMap.Lookup(uint32(0), &value))) qt.Assert(t, qt.Equals(value, ksyms["bpf_init"])) qt.Assert(t, qt.IsNil(obj.ArrayMap.Lookup(uint32(1), &value))) qt.Assert(t, qt.Equals(value, ksyms["bpf_trace_run1"])) } func TestKsymWeakMissing(t *testing.T) { file := testutils.NativeFile(t, "testdata/ksym-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) var obj struct { Main *Program `ebpf:"ksym_missing_test"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) defer obj.Main.Close() ret := mustRun(t, obj.Main, nil) qt.Assert(t, qt.Equals(ret, 1)) } func TestKfunc(t *testing.T) { testutils.SkipOnOldKernel(t, "5.18", "kfunc support") file := testutils.NativeFile(t, "testdata/kfunc-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var obj struct { Main *Program `ebpf:"call_kfunc"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("%+v", err) } defer obj.Main.Close() ret := mustRun(t, obj.Main, nil) if ret != 1 { t.Fatalf("Expected kfunc to return value 1, got %d", ret) } } func TestWeakKfunc(t *testing.T) { testutils.SkipOnOldKernel(t, "5.18", "kfunc support") // CAP_SYS_ADMIN is required to load kfuncs implemented in kernel modules. // Assert that when kfuncs are weak, loading still works without the capability. // CAP_BPF and CAP_PERFMON are still required to load BPF raw tracepoints programs // such as the one in this test. testutils.WithCapabilities(t, []testutils.Capability{testutils.CAP_BPF, testutils.CAP_PERFMON}, func() { file := testutils.NativeFile(t, "testdata/kfunc-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var obj struct { Missing *Program `ebpf:"weak_kfunc_missing"` Calling *Program `ebpf:"call_weak_kfunc"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("%+v", err) } defer obj.Missing.Close() defer obj.Calling.Close() }) } func TestInvalidKfunc(t *testing.T) { testutils.SkipOnOldKernel(t, "5.18", "kfunc support") requireTestmod(t) file := testutils.NativeFile(t, "testdata/invalid-kfunc-%s.elf") coll, err := LoadCollection(file) if err == nil { coll.Close() t.Fatal("Expected an error") } var ike *incompatibleKfuncError if !errors.As(err, &ike) { t.Fatalf("Expected an error wrapping incompatibleKfuncError, got %s", err) } } func TestKfuncKmod(t *testing.T) { testutils.SkipOnOldKernel(t, "5.18", "Kernel module function calls") requireTestmod(t) file := testutils.NativeFile(t, "testdata/kfunc-kmod-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var obj struct { Main *Program `ebpf:"call_kfunc"` } err = spec.LoadAndAssign(&obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("%v+", err) } defer obj.Main.Close() ret := mustRun(t, obj.Main, nil) if ret != 1 { t.Fatalf("Expected kfunc to return value 1, got %d", ret) } } func TestSubprogRelocation(t *testing.T) { testutils.SkipOnOldKernel(t, "5.13", "bpf_for_each_map_elem") file := testutils.NativeFile(t, "testdata/subprog_reloc-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } var obj struct { Main *Program `ebpf:"fp_relocation"` HashMap *Map `ebpf:"hash_map"` } err = loadAndAssign(t, spec, &obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Main.Close() defer obj.HashMap.Close() ret := mustRun(t, obj.Main, nil) if ret != 42 { t.Fatalf("Expected subprog reloc to return value 42, got %d", ret) } } func TestUnassignedProgArray(t *testing.T) { file := testutils.NativeFile(t, "testdata/btf_map_init-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } // tail_main references a ProgArray that is not being assigned // to this struct. Normally, this would clear all its entries // and make any tail calls into the ProgArray result in a miss. // The library needs to explicitly refuse such operations. var obj struct { TailMain *Program `ebpf:"tail_main"` // ProgArray *Map `ebpf:"prog_array_init"` } err = loadAndAssign(t, spec, &obj, nil) testutils.SkipIfNotSupported(t, err) defer obj.TailMain.Close() qt.Assert(t, qt.IsNotNil(err)) } func TestIPRoute2Compat(t *testing.T) { file := testutils.NativeFile(t, "testdata/iproute2_map_compat-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } ms, ok := spec.Maps["hash_map"] if !ok { t.Fatal("Map hash_map not found") } var id, pinning, innerID, innerIndex uint32 if ms.Extra == nil { t.Fatal("missing extra bytes") } switch { case binary.Read(ms.Extra, spec.ByteOrder, &id) != nil: t.Fatal("missing id") case binary.Read(ms.Extra, spec.ByteOrder, &pinning) != nil: t.Fatal("missing pinning") case binary.Read(ms.Extra, spec.ByteOrder, &innerID) != nil: t.Fatal("missing inner_id") case binary.Read(ms.Extra, spec.ByteOrder, &innerIndex) != nil: t.Fatal("missing inner_idx") } if id != 0 || innerID != 0 || innerIndex != 0 { t.Fatal("expecting id, inner_id and inner_idx to be zero") } if pinning != 2 { t.Fatal("expecting pinning field to be 2 (PIN_GLOBAL_NS)") } // iproute2 (tc) pins maps in /sys/fs/bpf/tc/globals with PIN_GLOBAL_NS, // which needs to be configured in this library using MapOptions.PinPath. // For the sake of the test, we use a tempdir on bpffs below. ms.Pinning = PinByName coll, err := NewCollectionWithOptions(spec, CollectionOptions{ Maps: MapOptions{ PinPath: testutils.TempBPFFS(t), }, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create collection:", err) } coll.Close() } func TestArena(t *testing.T) { file := testutils.NativeFile(t, "testdata/arena-%s.elf") coll, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) want := &CollectionSpec{ Maps: map[string]*MapSpec{ "arena": { Name: "arena", Type: Arena, MaxEntries: 100, Flags: sys.BPF_F_MMAPABLE, MapExtra: 1 << 44, }, }, Programs: map[string]*ProgramSpec{}, Variables: map[string]*VariableSpec{}, } qt.Assert(t, qt.CmpEquals(coll, want, csCmpOpts)) testutils.SkipOnOldKernel(t, "6.9", "arena maps") mustNewCollection(t, coll, nil) } func TestStructOps(t *testing.T) { file := testutils.NativeFile(t, "testdata/struct_ops-%s.elf") coll, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) userData := []byte{ // test_1 func ptr (8B) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // test_2 func ptr (8B) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data (4B) + padding (4B) 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, } want := &CollectionSpec{ Maps: map[string]*MapSpec{ "testmod_ops": { Name: "testmod_ops", Type: StructOpsMap, MaxEntries: 1, Flags: sys.BPF_F_LINK, Key: &btf.Int{Size: 4}, KeySize: 4, ValueSize: 24, Value: &btf.Struct{ Name: "bpf_testmod_ops", Size: 24, Members: []btf.Member{ { Name: "test_1", Type: &btf.Pointer{ Target: &btf.FuncProto{ Params: []btf.FuncParam{}, Return: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}}}, Offset: 0, }, { Name: "test_2", Type: &btf.Pointer{ Target: &btf.FuncProto{ Params: []btf.FuncParam{ {Type: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}}, {Type: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}}, }, Return: (*btf.Void)(nil), }, }, Offset: 64, }, { Name: "data", Type: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}, Offset: 128, // bits }, }, }, Contents: []MapKV{ { Key: uint32(0), Value: userData, }, }, }, }, Programs: map[string]*ProgramSpec{ "test_1": { Name: "test_1", Type: StructOps, AttachTo: "bpf_testmod_ops:test_1", License: "GPL", SectionName: "struct_ops/test_1", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }, }, Variables: map[string]*VariableSpec{}, } testModOps, ok := coll.Maps["testmod_ops"] if !ok { t.Fatalf("testmod_ops doesn't exist") } data, ok := testModOps.Contents[0].Value.([]byte) if !ok { t.Fatalf("Contents[0].Value should be an array of byte") } qt.Assert(t, qt.CmpEquals(coll.Programs, want.Programs, csCmpOpts)) qt.Assert(t, qt.CmpEquals(coll.Maps, want.Maps, csCmpOpts)) qt.Assert(t, qt.CmpEquals(testModOps.Value, want.Maps["testmod_ops"].Value, csCmpOpts)) qt.Assert(t, qt.CmpEquals(data, userData, csCmpOpts)) } var ( elfPath = flag.String("elfs", os.Getenv("CI_KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $CI_KERNEL_SELFTESTS)") elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested") ) func TestLibBPFCompat(t *testing.T) { if *elfPath == "" { // Specify the path to the directory containing the eBPF for // the kernel's selftests if you want to run this test. // As of 5.2 that is tools/testing/selftests/bpf/ t.Skip("No path specified") } load := func(t *testing.T, spec *CollectionSpec, opts CollectionOptions, valid bool) { // Disable retrying a program load with the log enabled, it leads // to OOM kills. opts.Programs.LogDisabled = true coll, err := NewCollectionWithOptions(spec, opts) testutils.SkipIfNotSupported(t, err) var errno syscall.Errno if errors.As(err, &errno) { // This error is most likely from a syscall and caused by us not // replicating some fixups done in the selftests or the test // intentionally failing. This is expected, so skip the test // instead of failing. t.Skip("Skipping since the kernel rejected the program:", err) } if err == nil { coll.Close() } if !valid { if err == nil { t.Fatal("Expected an error during load") } } else if err != nil { t.Fatal("Error during loading:", err) } } files := testutils.Glob(t, filepath.Join(*elfPath, *elfPattern), // These files are only used as a source of btf. "btf__core_reloc_*", ) testutils.Files(t, files, func(t *testing.T, path string) { name := selftestName(path) switch name { case "test_map_in_map", "test_select_reuseport_kern": t.Skip("Skipping due to missing InnerMap in map definition") case "test_core_autosize": t.Skip("Skipping since the test generates dynamic BTF") case "test_static_linked": t.Skip("Skipping since .text contains 'subprog' twice") case "netif_receive_skb", "local_kptr_stash", "local_kptr_stash_fail", "type_cast", "preempted_bpf_ma_op", "percpu_alloc_fail": // Error message like // fixup for CORERelocation(local_type_id, Struct:"bin_data"[0], // local_id=27): invalid immediate 31, expected 27 (fixup: local_type_id=27->1) // See https://github.com/cilium/ebpf/issues/739 t.Skip("Skipping due to bug in libbpf type deduplication") case "test_usdt", "test_urandom_usdt", "test_usdt_multispec": t.Skip("Skipping due to missing support for usdt.bpf.h") case "lsm_cgroup", "bpf_iter_ipv6_route", "test_core_extern", "profiler1", "profiler2", "profiler3": t.Skip("Skipping due to using weak CONFIG_* variables") case "linked_maps", "linked_maps1", "linked_maps2", "linked_funcs1", "linked_funcs2", "test_subskeleton", "test_subskeleton_lib": t.Skip("Skipping due to relying on cross ELF linking") case "test_log_fixup": t.Skip("Skipping due to intentionally broken CO-RE relocations") } t.Parallel() spec, err := LoadCollectionSpec(path) testutils.SkipIfNotSupported(t, err) if errors.Is(err, errUnsupportedBinding) { t.Skip(err) } if err != nil { t.Fatal(err) } switch name { case "test_sk_assign": // Test contains a legacy iproute2 bpf_elf_map definition. for _, m := range spec.Maps { if m.Extra == nil || m.Extra.Len() == 0 { t.Fatalf("Expected extra bytes in map %s", m.Name) } m.Extra = nil } case "fexit_bpf2bpf", "freplace_get_constant", "freplace_global_func": loadTargetProgram(t, spec, "test_pkt_access.bpf.o", "test_pkt_access") case "freplace_cls_redirect": loadTargetProgram(t, spec, "test_cls_redirect.bpf.o", "cls_redirect") case "test_trace_ext": loadTargetProgram(t, spec, "test_pkt_md_access.bpf.o", "test_pkt_md_access") case "freplace_progmap": loadTargetProgram(t, spec, "xdp_dummy.bpf.o", "xdp_dummy_prog") if prog := spec.Programs["xdp_cpumap_prog"]; prog.AttachTo == "" { prog.AttachTo = "xdp_dummy_prog" } case "freplace_attach_probe": // Looks like the test should have a target, but 6.6 selftests don't // seem to be using it. } var opts CollectionOptions for _, mapSpec := range spec.Maps { if mapSpec.Pinning != PinNone { opts.Maps.PinPath = testutils.TempBPFFS(t) break } } coreFiles := sourceOfBTF(t, path) if len(coreFiles) == 0 { // NB: test_core_reloc_kernel.o doesn't have dedicated BTF and // therefore goes via this code path. load(t, spec, opts, true) return } for _, coreFile := range coreFiles { name := selftestName(coreFile) t.Run(name, func(t *testing.T) { // Some files like btf__core_reloc_arrays___err_too_small.o // trigger an error on purpose. Use the name to infer whether // the test should succeed. var valid bool switch name { case "btf__core_reloc_existence___err_wrong_arr_kind", "btf__core_reloc_existence___err_wrong_arr_value_type", "btf__core_reloc_existence___err_wrong_int_kind", "btf__core_reloc_existence___err_wrong_int_sz", "btf__core_reloc_existence___err_wrong_int_type", "btf__core_reloc_existence___err_wrong_struct_type": // These tests are buggy upstream, see https://lore.kernel.org/bpf/20210420111639.155580-1-lmb@cloudflare.com/ valid = true case "btf__core_reloc_ints___err_wrong_sz_16", "btf__core_reloc_ints___err_wrong_sz_32", "btf__core_reloc_ints___err_wrong_sz_64", "btf__core_reloc_ints___err_wrong_sz_8", "btf__core_reloc_arrays___err_wrong_val_type1", "btf__core_reloc_arrays___err_wrong_val_type2": // These tests are valid according to current libbpf behaviour, // see commit 42765ede5c54ca915de5bfeab83be97207e46f68. valid = true case "btf__core_reloc_type_id___missing_targets", "btf__core_reloc_flavors__err_wrong_name": valid = false case "btf__core_reloc_ints___err_bitfield": // Bitfields are now valid. valid = true default: valid = !strings.Contains(name, "___err_") } fh, err := os.Open(coreFile) if err != nil { t.Fatal(err) } defer fh.Close() btfSpec, err := btf.LoadSpec(coreFile) if err != nil { t.Fatal(err) } opts := opts // copy opts.Programs.KernelTypes = btfSpec load(t, spec, opts, valid) }) } }) } func loadTargetProgram(tb testing.TB, spec *CollectionSpec, file, program string) { targetSpec, err := LoadCollectionSpec(filepath.Join(*elfPath, file)) if errors.Is(err, os.ErrNotExist) && strings.HasSuffix(file, ".bpf.o") { // Prior to v6.1 BPF ELF used a plain .o suffix. file = strings.TrimSuffix(file, ".bpf.o") + ".o" targetSpec, err = LoadCollectionSpec(filepath.Join(*elfPath, file)) } if err != nil { tb.Fatalf("Can't read %s: %s", file, err) } qt.Assert(tb, qt.IsNotNil(targetSpec.Programs[program])) coll, err := NewCollectionWithOptions(targetSpec, CollectionOptions{ Programs: ProgramOptions{LogDisabled: true}, }) if err != nil { tb.Fatalf("Can't load target: %s", err) } tb.Cleanup(func() { coll.Close() }) target := coll.Programs[program] for _, prog := range spec.Programs { if prog.Type == Extension && prog.AttachType == AttachNone { prog.AttachTarget = target continue } if prog.Type == Tracing { switch prog.AttachType { case AttachTraceFEntry, AttachTraceFExit, AttachModifyReturn: prog.AttachTarget = target continue } } } } func sourceOfBTF(tb testing.TB, path string) []string { const testPrefix = "test_core_reloc_" const btfPrefix = "btf__core_reloc_" dir, base := filepath.Split(path) if !strings.HasPrefix(base, testPrefix) { return nil } base = strings.TrimSuffix(base[len(testPrefix):], ".o") switch base { case "bitfields_direct", "bitfields_probed": base = "bitfields" } return testutils.Glob(tb, filepath.Join(dir, btfPrefix+base+"*.o")) } func TestELFSectionProgramTypes(t *testing.T) { type testcase struct { Section string ProgramType ProgramType AttachType AttachType Flags uint32 Extra string } testcases := []testcase{ {"socket", SocketFilter, AttachNone, 0, ""}, {"socket/garbage", SocketFilter, AttachNone, 0, ""}, {"sk_reuseport/migrate", SkReuseport, AttachSkReuseportSelectOrMigrate, 0, ""}, {"sk_reuseport", SkReuseport, AttachSkReuseportSelect, 0, ""}, {"kprobe/", Kprobe, AttachNone, 0, ""}, {"kprobe/func", Kprobe, AttachNone, 0, "func"}, {"uprobe/", Kprobe, AttachNone, 0, ""}, {"kretprobe/", Kprobe, AttachNone, 0, ""}, {"uretprobe/", Kprobe, AttachNone, 0, ""}, {"tc", SchedCLS, AttachNone, 0, ""}, {"classifier", SchedCLS, AttachNone, 0, ""}, {"action", SchedACT, AttachNone, 0, ""}, {"tracepoint/", TracePoint, AttachNone, 0, ""}, {"tp/", TracePoint, AttachNone, 0, ""}, {"raw_tracepoint/", RawTracepoint, AttachNone, 0, ""}, {"raw_tp/", RawTracepoint, AttachNone, 0, ""}, {"raw_tracepoint.w/", RawTracepointWritable, AttachNone, 0, ""}, {"raw_tp.w/", RawTracepointWritable, AttachNone, 0, ""}, {"tp_btf/", Tracing, AttachTraceRawTp, 0, ""}, {"fentry/", Tracing, AttachTraceFEntry, 0, ""}, {"fmod_ret/", Tracing, AttachModifyReturn, 0, ""}, {"fexit/", Tracing, AttachTraceFExit, 0, ""}, {"fentry.s/", Tracing, AttachTraceFEntry, sys.BPF_F_SLEEPABLE, ""}, {"fmod_ret.s/", Tracing, AttachModifyReturn, sys.BPF_F_SLEEPABLE, ""}, {"fexit.s/", Tracing, AttachTraceFExit, sys.BPF_F_SLEEPABLE, ""}, {"freplace/", Extension, AttachNone, 0, ""}, {"lsm/foo", LSM, AttachLSMMac, 0, "foo"}, {"lsm.s/foo", LSM, AttachLSMMac, sys.BPF_F_SLEEPABLE, "foo"}, {"iter/bpf_map", Tracing, AttachTraceIter, 0, "bpf_map"}, {"iter.s/", Tracing, AttachTraceIter, sys.BPF_F_SLEEPABLE, ""}, {"syscall", Syscall, AttachNone, sys.BPF_F_SLEEPABLE, ""}, {"xdp.frags/devmap", XDP, AttachXDPDevMap, sys.BPF_F_XDP_HAS_FRAGS, ""}, {"xdp/devmap", XDP, AttachXDPDevMap, 0, ""}, {"xdp.frags/cpumap", XDP, AttachXDPCPUMap, sys.BPF_F_XDP_HAS_FRAGS, ""}, {"xdp/cpumap", XDP, AttachXDPCPUMap, 0, ""}, {"xdp.frags", XDP, AttachXDP, sys.BPF_F_XDP_HAS_FRAGS, ""}, {"xdp", XDP, AttachXDP, 0, ""}, {"perf_event", PerfEvent, AttachNone, 0, ""}, {"lwt_in", LWTIn, AttachNone, 0, ""}, {"lwt_out", LWTOut, AttachNone, 0, ""}, {"lwt_xmit", LWTXmit, AttachNone, 0, ""}, {"lwt_seg6local", LWTSeg6Local, AttachNone, 0, ""}, {"cgroup_skb/ingress", CGroupSKB, AttachCGroupInetIngress, 0, ""}, {"cgroup_skb/egress", CGroupSKB, AttachCGroupInetEgress, 0, ""}, {"cgroup/skb", CGroupSKB, AttachNone, 0, ""}, {"cgroup/sock_create", CGroupSock, AttachCGroupInetSockCreate, 0, ""}, {"cgroup/sock_release", CGroupSock, AttachCgroupInetSockRelease, 0, ""}, {"cgroup/sock", CGroupSock, AttachCGroupInetSockCreate, 0, ""}, {"cgroup/post_bind4", CGroupSock, AttachCGroupInet4PostBind, 0, ""}, {"cgroup/post_bind6", CGroupSock, AttachCGroupInet6PostBind, 0, ""}, {"cgroup/dev", CGroupDevice, AttachCGroupDevice, 0, ""}, {"sockops", SockOps, AttachCGroupSockOps, 0, ""}, {"sk_skb/stream_parser", SkSKB, AttachSkSKBStreamParser, 0, ""}, {"sk_skb/stream_verdict", SkSKB, AttachSkSKBStreamVerdict, 0, ""}, {"sk_skb/stream_verdict/foo", SkSKB, AttachSkSKBStreamVerdict, 0, ""}, {"sk_skb", SkSKB, AttachNone, 0, ""}, {"sk_skb/bar", SkSKB, AttachNone, 0, ""}, {"sk_msg", SkMsg, AttachSkMsgVerdict, 0, ""}, {"lirc_mode2", LircMode2, AttachLircMode2, 0, ""}, {"flow_dissector", FlowDissector, AttachFlowDissector, 0, ""}, {"cgroup/bind4", CGroupSockAddr, AttachCGroupInet4Bind, 0, ""}, {"cgroup/bind6", CGroupSockAddr, AttachCGroupInet6Bind, 0, ""}, {"cgroup/connect4", CGroupSockAddr, AttachCGroupInet4Connect, 0, ""}, {"cgroup/connect6", CGroupSockAddr, AttachCGroupInet6Connect, 0, ""}, {"cgroup/sendmsg4", CGroupSockAddr, AttachCGroupUDP4Sendmsg, 0, ""}, {"cgroup/sendmsg6", CGroupSockAddr, AttachCGroupUDP6Sendmsg, 0, ""}, {"cgroup/recvmsg4", CGroupSockAddr, AttachCGroupUDP4Recvmsg, 0, ""}, {"cgroup/recvmsg6", CGroupSockAddr, AttachCGroupUDP6Recvmsg, 0, ""}, {"cgroup/getpeername4", CGroupSockAddr, AttachCgroupInet4GetPeername, 0, ""}, {"cgroup/getpeername6", CGroupSockAddr, AttachCgroupInet6GetPeername, 0, ""}, {"cgroup/getsockname4", CGroupSockAddr, AttachCgroupInet4GetSockname, 0, ""}, {"cgroup/getsockname6", CGroupSockAddr, AttachCgroupInet6GetSockname, 0, ""}, {"cgroup/sysctl", CGroupSysctl, AttachCGroupSysctl, 0, ""}, {"cgroup/getsockopt", CGroupSockopt, AttachCGroupGetsockopt, 0, ""}, {"cgroup/setsockopt", CGroupSockopt, AttachCGroupSetsockopt, 0, ""}, {"sk_lookup/", SkLookup, AttachSkLookup, 0, ""}, {"kprobe.multi", Kprobe, AttachTraceKprobeMulti, 0, ""}, {"kretprobe.multi", Kprobe, AttachTraceKprobeMulti, 0, ""}, {"struct_ops", StructOps, AttachNone, 0, ""}, {"struct_ops.s", StructOps, AttachNone, sys.BPF_F_SLEEPABLE, ""}, {"struct_ops/foo", StructOps, AttachNone, 0, ""}, } for _, tc := range testcases { t.Run(tc.Section, func(t *testing.T) { pt, at, fl, extra := getProgType(tc.Section) have := testcase{tc.Section, pt, at, fl, extra} qt.Assert(t, qt.DeepEquals(have, tc)) }) } } func TestMatchSectionName(t *testing.T) { for _, testcase := range []struct { pattern string input string matches bool extra string }{ {"prefix/", "prefix/", true, ""}, {"prefix/", "prefix/a", true, "a"}, {"prefix/", "prefix/b", true, "b"}, {"prefix/", "prefix", false, ""}, {"prefix/", "junk", false, ""}, {"prefix+", "prefix/", true, ""}, {"prefix+", "prefix/a", true, "a"}, {"prefix+", "prefix/b", true, "b"}, {"prefix+", "prefix", true, ""}, {"prefix+", "junk", false, ""}, {"exact", "exact", true, ""}, {"exact", "exact/", true, ""}, {"exact", "exact/a", true, ""}, {"exact", "exactement", true, ""}, {"exact", "junk", false, ""}, } { name := fmt.Sprintf("%s:%s", testcase.pattern, testcase.input) t.Run(name, func(t *testing.T) { extra, matches := matchSectionName(testcase.input, testcase.pattern) qt.Assert(t, qt.Equals(matches, testcase.matches)) if testcase.matches { qt.Assert(t, qt.Equals(extra, testcase.extra)) } }) } } // selftestName takes a path to a file and derives a canonical name from it. // // It strips various suffixes used by the selftest build system. func selftestName(path string) string { file := filepath.Base(path) name := strings.TrimSuffix(file, ".o") // Strip various suffixes. // Various linking suffixes. name = strings.TrimSuffix(name, ".linked3") name = strings.TrimSuffix(name, ".llinked1") name = strings.TrimSuffix(name, ".llinked2") name = strings.TrimSuffix(name, ".llinked3") // v6.1 started adding .bpf to all BPF ELF. name = strings.TrimSuffix(name, ".bpf") return name } golang-github-cilium-ebpf-0.21.0+ds1/elf_sections.go000066400000000000000000000212371520243672000222100ustar00rootroot00000000000000// Code generated by internal/cmd/gensections.awk; DO NOT EDIT. package ebpf // Code in this file is derived from libbpf, available under BSD-2-Clause. import "github.com/cilium/ebpf/internal/sys" var elfSectionDefs = []libbpfElfSectionDef{ {"socket", sys.BPF_PROG_TYPE_SOCKET_FILTER, 0, _SEC_NONE}, {"sk_reuseport/migrate", sys.BPF_PROG_TYPE_SK_REUSEPORT, sys.BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, _SEC_ATTACHABLE}, {"sk_reuseport", sys.BPF_PROG_TYPE_SK_REUSEPORT, sys.BPF_SK_REUSEPORT_SELECT, _SEC_ATTACHABLE}, {"kprobe+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE}, {"uprobe+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE}, {"uprobe.s+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_SLEEPABLE}, {"kretprobe+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE}, {"uretprobe+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE}, {"uretprobe.s+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_SLEEPABLE}, {"kprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE}, {"kretprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE}, {"kprobe.session+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_SESSION, _SEC_NONE}, {"uprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE}, {"uretprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE}, {"uprobe.session+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_SESSION, _SEC_NONE}, {"uprobe.multi.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE}, {"uretprobe.multi.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE}, {"uprobe.session.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_SESSION, _SEC_SLEEPABLE}, {"ksyscall+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE}, {"kretsyscall+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE}, {"usdt+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_USDT}, {"usdt.s+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_USDT | _SEC_SLEEPABLE}, {"tc/ingress", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_INGRESS, _SEC_NONE}, {"tc/egress", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_EGRESS, _SEC_NONE}, {"tcx/ingress", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_INGRESS, _SEC_NONE}, {"tcx/egress", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_EGRESS, _SEC_NONE}, {"tc", sys.BPF_PROG_TYPE_SCHED_CLS, 0, _SEC_NONE}, {"classifier", sys.BPF_PROG_TYPE_SCHED_CLS, 0, _SEC_NONE}, {"action", sys.BPF_PROG_TYPE_SCHED_ACT, 0, _SEC_NONE}, {"netkit/primary", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_NETKIT_PRIMARY, _SEC_NONE}, {"netkit/peer", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_NETKIT_PEER, _SEC_NONE}, {"tracepoint+", sys.BPF_PROG_TYPE_TRACEPOINT, 0, _SEC_NONE}, {"tp+", sys.BPF_PROG_TYPE_TRACEPOINT, 0, _SEC_NONE}, {"raw_tracepoint+", sys.BPF_PROG_TYPE_RAW_TRACEPOINT, 0, _SEC_NONE}, {"raw_tp+", sys.BPF_PROG_TYPE_RAW_TRACEPOINT, 0, _SEC_NONE}, {"raw_tracepoint.w+", sys.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, 0, _SEC_NONE}, {"raw_tp.w+", sys.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, 0, _SEC_NONE}, {"tp_btf+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_RAW_TP, _SEC_ATTACH_BTF}, {"fentry+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FENTRY, _SEC_ATTACH_BTF}, {"fmod_ret+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_MODIFY_RETURN, _SEC_ATTACH_BTF}, {"fexit+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FEXIT, _SEC_ATTACH_BTF}, {"fentry.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FENTRY, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"fmod_ret.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_MODIFY_RETURN, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"fexit.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FEXIT, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"freplace+", sys.BPF_PROG_TYPE_EXT, 0, _SEC_ATTACH_BTF}, {"lsm+", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF}, {"lsm.s+", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"lsm_cgroup+", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_CGROUP, _SEC_ATTACH_BTF}, {"iter+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_ITER, _SEC_ATTACH_BTF}, {"iter.s+", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_ITER, _SEC_ATTACH_BTF | _SEC_SLEEPABLE}, {"syscall", sys.BPF_PROG_TYPE_SYSCALL, 0, _SEC_SLEEPABLE}, {"xdp.frags/devmap", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_XDP_FRAGS}, {"xdp/devmap", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_ATTACHABLE}, {"xdp.frags/cpumap", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_XDP_FRAGS}, {"xdp/cpumap", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_ATTACHABLE}, {"xdp.frags", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_XDP_FRAGS}, {"xdp", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_ATTACHABLE_OPT}, {"perf_event", sys.BPF_PROG_TYPE_PERF_EVENT, 0, _SEC_NONE}, {"lwt_in", sys.BPF_PROG_TYPE_LWT_IN, 0, _SEC_NONE}, {"lwt_out", sys.BPF_PROG_TYPE_LWT_OUT, 0, _SEC_NONE}, {"lwt_xmit", sys.BPF_PROG_TYPE_LWT_XMIT, 0, _SEC_NONE}, {"lwt_seg6local", sys.BPF_PROG_TYPE_LWT_SEG6LOCAL, 0, _SEC_NONE}, {"sockops", sys.BPF_PROG_TYPE_SOCK_OPS, sys.BPF_CGROUP_SOCK_OPS, _SEC_ATTACHABLE_OPT}, {"sk_skb/stream_parser", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_PARSER, _SEC_ATTACHABLE_OPT}, {"sk_skb/stream_verdict", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_VERDICT, _SEC_ATTACHABLE_OPT}, {"sk_skb/verdict", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_VERDICT, _SEC_ATTACHABLE_OPT}, {"sk_skb", sys.BPF_PROG_TYPE_SK_SKB, 0, _SEC_NONE}, {"sk_msg", sys.BPF_PROG_TYPE_SK_MSG, sys.BPF_SK_MSG_VERDICT, _SEC_ATTACHABLE_OPT}, {"lirc_mode2", sys.BPF_PROG_TYPE_LIRC_MODE2, sys.BPF_LIRC_MODE2, _SEC_ATTACHABLE_OPT}, {"flow_dissector", sys.BPF_PROG_TYPE_FLOW_DISSECTOR, sys.BPF_FLOW_DISSECTOR, _SEC_ATTACHABLE_OPT}, {"cgroup_skb/ingress", sys.BPF_PROG_TYPE_CGROUP_SKB, sys.BPF_CGROUP_INET_INGRESS, _SEC_ATTACHABLE_OPT}, {"cgroup_skb/egress", sys.BPF_PROG_TYPE_CGROUP_SKB, sys.BPF_CGROUP_INET_EGRESS, _SEC_ATTACHABLE_OPT}, {"cgroup/skb", sys.BPF_PROG_TYPE_CGROUP_SKB, 0, _SEC_NONE}, {"cgroup/sock_create", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET_SOCK_CREATE, _SEC_ATTACHABLE}, {"cgroup/sock_release", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET_SOCK_RELEASE, _SEC_ATTACHABLE}, {"cgroup/sock", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET_SOCK_CREATE, _SEC_ATTACHABLE_OPT}, {"cgroup/post_bind4", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET4_POST_BIND, _SEC_ATTACHABLE}, {"cgroup/post_bind6", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET6_POST_BIND, _SEC_ATTACHABLE}, {"cgroup/bind4", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_BIND, _SEC_ATTACHABLE}, {"cgroup/bind6", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_BIND, _SEC_ATTACHABLE}, {"cgroup/connect4", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_CONNECT, _SEC_ATTACHABLE}, {"cgroup/connect6", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_CONNECT, _SEC_ATTACHABLE}, {"cgroup/connect_unix", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_CONNECT, _SEC_ATTACHABLE}, {"cgroup/sendmsg4", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP4_SENDMSG, _SEC_ATTACHABLE}, {"cgroup/sendmsg6", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP6_SENDMSG, _SEC_ATTACHABLE}, {"cgroup/sendmsg_unix", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_SENDMSG, _SEC_ATTACHABLE}, {"cgroup/recvmsg4", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP4_RECVMSG, _SEC_ATTACHABLE}, {"cgroup/recvmsg6", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP6_RECVMSG, _SEC_ATTACHABLE}, {"cgroup/recvmsg_unix", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_RECVMSG, _SEC_ATTACHABLE}, {"cgroup/getpeername4", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_GETPEERNAME, _SEC_ATTACHABLE}, {"cgroup/getpeername6", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_GETPEERNAME, _SEC_ATTACHABLE}, {"cgroup/getpeername_unix", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_GETPEERNAME, _SEC_ATTACHABLE}, {"cgroup/getsockname4", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_GETSOCKNAME, _SEC_ATTACHABLE}, {"cgroup/getsockname6", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_GETSOCKNAME, _SEC_ATTACHABLE}, {"cgroup/getsockname_unix", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_GETSOCKNAME, _SEC_ATTACHABLE}, {"cgroup/sysctl", sys.BPF_PROG_TYPE_CGROUP_SYSCTL, sys.BPF_CGROUP_SYSCTL, _SEC_ATTACHABLE}, {"cgroup/getsockopt", sys.BPF_PROG_TYPE_CGROUP_SOCKOPT, sys.BPF_CGROUP_GETSOCKOPT, _SEC_ATTACHABLE}, {"cgroup/setsockopt", sys.BPF_PROG_TYPE_CGROUP_SOCKOPT, sys.BPF_CGROUP_SETSOCKOPT, _SEC_ATTACHABLE}, {"cgroup/dev", sys.BPF_PROG_TYPE_CGROUP_DEVICE, sys.BPF_CGROUP_DEVICE, _SEC_ATTACHABLE_OPT}, {"struct_ops+", sys.BPF_PROG_TYPE_STRUCT_OPS, 0, _SEC_NONE}, {"struct_ops.s+", sys.BPF_PROG_TYPE_STRUCT_OPS, 0, _SEC_SLEEPABLE}, {"sk_lookup", sys.BPF_PROG_TYPE_SK_LOOKUP, sys.BPF_SK_LOOKUP, _SEC_ATTACHABLE}, {"netfilter", sys.BPF_PROG_TYPE_NETFILTER, sys.BPF_NETFILTER, _SEC_NONE}, } golang-github-cilium-ebpf-0.21.0+ds1/example_sock_elf_test.go000066400000000000000000000177351520243672000241020ustar00rootroot00000000000000//go:build linux package ebpf_test import ( "bytes" "encoding/binary" "flag" "fmt" "syscall" "time" "unsafe" "github.com/cilium/ebpf" ) var program = [...]byte{ 0177, 0105, 0114, 0106, 0002, 0001, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0367, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0340, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0010, 0000, 0001, 0000, 0277, 0026, 0000, 0000, 0000, 0000, 0000, 0000, 0060, 0000, 0000, 0000, 0027, 0000, 0000, 0000, 0143, 0012, 0374, 0377, 0000, 0000, 0000, 0000, 0141, 0141, 0004, 0000, 0000, 0000, 0000, 0000, 0125, 0001, 0010, 0000, 0004, 0000, 0000, 0000, 0277, 0242, 0000, 0000, 0000, 0000, 0000, 0000, 0007, 0002, 0000, 0000, 0374, 0377, 0377, 0377, 0030, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0205, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0025, 0000, 0002, 0000, 0000, 0000, 0000, 0000, 0141, 0141, 0000, 0000, 0000, 0000, 0000, 0000, 0333, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0267, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0225, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0107, 0120, 0114, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0065, 0000, 0000, 0000, 0000, 0000, 0003, 0000, 0150, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0034, 0000, 0000, 0000, 0020, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0110, 0000, 0000, 0000, 0020, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0014, 0000, 0000, 0000, 0020, 0000, 0005, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0023, 0000, 0000, 0000, 0020, 0000, 0005, 0000, 0024, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0070, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0056, 0164, 0145, 0170, 0164, 0000, 0155, 0141, 0160, 0163, 0000, 0155, 0171, 0137, 0155, 0141, 0160, 0000, 0164, 0145, 0163, 0164, 0137, 0155, 0141, 0160, 0000, 0137, 0154, 0151, 0143, 0145, 0156, 0163, 0145, 0000, 0056, 0163, 0164, 0162, 0164, 0141, 0142, 0000, 0056, 0163, 0171, 0155, 0164, 0141, 0142, 0000, 0114, 0102, 0102, 0060, 0137, 0063, 0000, 0056, 0162, 0145, 0154, 0163, 0157, 0143, 0153, 0145, 0164, 0061, 0000, 0142, 0160, 0146, 0137, 0160, 0162, 0157, 0147, 0061, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0045, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0210, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0122, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0170, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0074, 0000, 0000, 0000, 0011, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0170, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0007, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0007, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0270, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0050, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0035, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0340, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0055, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0350, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0220, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0030, 0000, 0000, 0000, 0000, 0000, 0000, 0000, } // ExampleSocketELF demonstrates how to load an eBPF program from an ELF, // and attach it to a raw socket. func Example_socketELF() { const SO_ATTACH_BPF = 50 index := flag.Int("index", 0, "specify ethernet index") flag.Parse() spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(program[:])) if err != nil { panic(err) } var objs struct { Prog *ebpf.Program `ebpf:"bpf_prog1"` Stats *ebpf.Map `ebpf:"my_map"` } if err := spec.LoadAndAssign(&objs, nil); err != nil { panic(err) } defer objs.Prog.Close() defer objs.Stats.Close() sock, err := openRawSock(*index) if err != nil { panic(err) } defer syscall.Close(sock) if err := syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, SO_ATTACH_BPF, objs.Prog.FD()); err != nil { panic(err) } fmt.Printf("Filtering on eth index: %d\n", *index) fmt.Println("Packet stats:") for { const ( ICMP = 0x01 TCP = 0x06 UDP = 0x11 ) time.Sleep(time.Second) var icmp uint64 var tcp uint64 var udp uint64 err := objs.Stats.Lookup(uint32(ICMP), &icmp) if err != nil { panic(err) } err = objs.Stats.Lookup(uint32(TCP), &tcp) if err != nil { panic(err) } err = objs.Stats.Lookup(uint32(UDP), &udp) if err != nil { panic(err) } fmt.Printf("\r\033[m\tICMP: %d TCP: %d UDP: %d", icmp, tcp, udp) } } func openRawSock(index int) (int, error) { sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, int(htons(syscall.ETH_P_ALL))) if err != nil { return 0, err } sll := syscall.SockaddrLinklayer{ Ifindex: index, Protocol: htons(syscall.ETH_P_ALL), } if err := syscall.Bind(sock, &sll); err != nil { return 0, err } return sock, nil } // htons converts the unsigned short integer hostshort from host byte order to network byte order. func htons(i uint16) uint16 { b := make([]byte, 2) binary.BigEndian.PutUint16(b, i) return *(*uint16)(unsafe.Pointer(&b[0])) } golang-github-cilium-ebpf-0.21.0+ds1/example_sock_extract_dist_test.go000066400000000000000000000120741520243672000260200ustar00rootroot00000000000000//go:build linux package ebpf_test // This code is derived from https://github.com/cloudflare/cloudflare-blog/tree/master/2018-03-ebpf // // Copyright (c) 2015-2017 Cloudflare, Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of the Cloudflare, Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import ( "fmt" "net" "syscall" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" ) // ExampleExtractDistance shows how to attach an eBPF socket filter to // extract the network distance of an IP host. func Example_extractDistance() { filter, TTLs, err := newDistanceFilter() if err != nil { panic(err) } defer filter.Close() defer TTLs.Close() // Attach filter before the call to connect() dialer := net.Dialer{ Control: func(network, address string, c syscall.RawConn) (err error) { const SO_ATTACH_BPF = 50 err = c.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_ATTACH_BPF, filter.FD()) }) return err }, } conn, err := dialer.Dial("tcp", "1.1.1.1:53") if err != nil { panic(err) } conn.Close() minDist, err := minDistance(TTLs) if err != nil { panic(err) } fmt.Println("1.1.1.1:53 is", minDist, "hops away") } func newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) { const ETH_P_IPV6 uint16 = 0x86DD ttls, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.Hash, KeySize: 4, ValueSize: 8, MaxEntries: 4, }) if err != nil { return nil, nil, err } insns := asm.Instructions{ // r1 has ctx // r0 = ctx[16] (aka protocol) asm.LoadMem(asm.R0, asm.R1, 16, asm.Word), // Perhaps ipv6 asm.LoadImm(asm.R2, int64(ETH_P_IPV6), asm.DWord), asm.HostTo(asm.BE, asm.R2, asm.Half), asm.JEq.Reg(asm.R0, asm.R2, "ipv6"), // otherwise assume ipv4 // 8th byte in IPv4 is TTL // LDABS requires ctx in R6 asm.Mov.Reg(asm.R6, asm.R1), asm.LoadAbs(-0x100000+8, asm.Byte), asm.Ja.Label("store-ttl"), // 7th byte in IPv6 is Hop count // LDABS requires ctx in R6 asm.Mov.Reg(asm.R6, asm.R1).WithSymbol("ipv6"), asm.LoadAbs(-0x100000+7, asm.Byte), // stash the load result into FP[-4] asm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).WithSymbol("store-ttl"), // stash the &FP[-4] into r2 asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), // r1 must point to map asm.LoadMapPtr(asm.R1, ttls.FD()), asm.FnMapLookupElem.Call(), // load ok? inc. Otherwise? jmp to mapupdate asm.JEq.Imm(asm.R0, 0, "update-map"), asm.Mov.Imm(asm.R1, 1), asm.StoreXAdd(asm.R0, asm.R1, asm.DWord), asm.Ja.Label("exit"), // MapUpdate // r1 has map ptr asm.LoadMapPtr(asm.R1, ttls.FD()).WithSymbol("update-map"), // r2 has key -> &FP[-4] asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), // r3 has value -> &FP[-16] , aka 1 asm.StoreImm(asm.RFP, -16, 1, asm.DWord), asm.Mov.Reg(asm.R3, asm.RFP), asm.Add.Imm(asm.R3, -16), // r4 has flags, 0 asm.Mov.Imm(asm.R4, 0), asm.FnMapUpdateElem.Call(), // set exit code to -1, don't trunc packet asm.Mov.Imm(asm.R0, -1).WithSymbol("exit"), asm.Return(), } prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "distance_filter", Type: ebpf.SocketFilter, License: "GPL", Instructions: insns, }) if err != nil { ttls.Close() return nil, nil, err } return prog, ttls, nil } func minDistance(TTLs *ebpf.Map) (int, error) { var ( entries = TTLs.Iterate() ttl uint32 minDist uint32 = 255 count uint64 ) for entries.Next(&ttl, &count) { var dist uint32 switch { case ttl > 128: dist = 255 - ttl case ttl > 64: dist = 128 - ttl case ttl > 32: dist = 64 - ttl default: dist = 32 - ttl } if minDist > dist { minDist = dist } } return int(minDist), entries.Err() } golang-github-cilium-ebpf-0.21.0+ds1/features/000077500000000000000000000000001520243672000210155ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/features/doc.go000066400000000000000000000015671520243672000221220ustar00rootroot00000000000000// Package features allows probing for BPF features available to the calling process. // // In general, the error return values from feature probes in this package // all have the following semantics unless otherwise specified: // // err == nil: The feature is available. // errors.Is(err, ebpf.ErrNotSupported): The feature is not available. // err != nil: Any errors encountered during probe execution, wrapped. // // Note that the latter case may include false negatives, and that resource // creation may succeed despite an error being returned. For example, some // map and program types cannot reliably be probed and will return an // inconclusive error. // // As a rule, only `nil` and `ebpf.ErrNotSupported` are conclusive. // // Probe results are cached by the library and persist throughout any changes // to the process' environment, like capability changes. package features golang-github-cilium-ebpf-0.21.0+ds1/features/link.go000066400000000000000000000077531520243672000223150ustar00rootroot00000000000000package features import ( "errors" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // HaveBPFLinkUprobeMulti probes the running kernel if uprobe_multi link is supported. // // See the package documentation for the meaning of the error return value. func HaveBPFLinkUprobeMulti() error { return haveBPFLinkUprobeMulti() } var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_upm_link", Type: ebpf.Kprobe, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, AttachType: ebpf.AttachTraceUprobeMulti, License: "MIT", }) if errors.Is(err, unix.E2BIG) { // Kernel doesn't support AttachType field. return ebpf.ErrNotSupported } if err != nil { return err } defer prog.Close() // We try to create uprobe multi link on '/' path which results in // error with -EBADF in case uprobe multi link is supported. fd, err := sys.LinkCreateUprobeMulti(&sys.LinkCreateUprobeMultiAttr{ ProgFd: uint32(prog.FD()), AttachType: sys.BPF_TRACE_UPROBE_MULTI, Path: sys.NewStringPointer("/"), Offsets: sys.SlicePointer([]uint64{0}), Count: 1, }) switch { case errors.Is(err, unix.EBADF): return nil case errors.Is(err, unix.EINVAL): return ebpf.ErrNotSupported case err != nil: return err } // should not happen fd.Close() return errors.New("successfully attached uprobe_multi to /, kernel bug?") }, "6.6") // HaveBPFLinkKprobeMulti probes the running kernel if kprobe_multi link is supported. // // See the package documentation for the meaning of the error return value. func HaveBPFLinkKprobeMulti() error { return haveBPFLinkKprobeMulti() } var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_kpm_link", Type: ebpf.Kprobe, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, AttachType: ebpf.AttachTraceKprobeMulti, License: "MIT", }) if errors.Is(err, unix.E2BIG) { // Kernel doesn't support AttachType field. return ebpf.ErrNotSupported } if err != nil { return err } defer prog.Close() fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{ ProgFd: uint32(prog.FD()), AttachType: sys.BPF_TRACE_KPROBE_MULTI, Count: 1, Syms: sys.NewStringSlicePointer([]string{"vprintk"}), }) switch { case errors.Is(err, unix.EINVAL): return ebpf.ErrNotSupported // If CONFIG_FPROBE isn't set. case errors.Is(err, unix.EOPNOTSUPP): return ebpf.ErrNotSupported case err != nil: return err } fd.Close() return nil }, "5.18") // HaveBPFLinkKprobeSession probes the running kernel if kprobe_session link is supported. // // See the package documentation for the meaning of the error return value. func HaveBPFLinkKprobeSession() error { return haveBPFLinkKprobeSession() } var haveBPFLinkKprobeSession = internal.NewFeatureTest("bpf_link_kprobe_session", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_kps_link", Type: ebpf.Kprobe, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, AttachType: ebpf.AttachTraceKprobeSession, License: "MIT", }) if errors.Is(err, unix.E2BIG) { // Kernel doesn't support AttachType field. return ebpf.ErrNotSupported } if err != nil { return err } defer prog.Close() fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{ ProgFd: uint32(prog.FD()), AttachType: sys.BPF_TRACE_KPROBE_SESSION, Count: 1, Syms: sys.NewStringSlicePointer([]string{"vprintk"}), }) switch { case errors.Is(err, unix.EINVAL): return ebpf.ErrNotSupported // If CONFIG_FPROBE isn't set. case errors.Is(err, unix.EOPNOTSUPP): return ebpf.ErrNotSupported case err != nil: return err } fd.Close() return nil }, "6.10") golang-github-cilium-ebpf-0.21.0+ds1/features/link_test.go000066400000000000000000000006301520243672000233370ustar00rootroot00000000000000package features import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveBPFLinkUprobeMulti(t *testing.T) { testutils.CheckFeatureTest(t, HaveBPFLinkUprobeMulti) } func TestHaveBPFLinkKprobeMulti(t *testing.T) { testutils.CheckFeatureTest(t, HaveBPFLinkKprobeMulti) } func TestHaveBPFLinkKprobeSession(t *testing.T) { testutils.CheckFeatureTest(t, HaveBPFLinkKprobeSession) } golang-github-cilium-ebpf-0.21.0+ds1/features/map.go000066400000000000000000000220531520243672000221230ustar00rootroot00000000000000package features import ( "errors" "fmt" "os" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // HaveMapType probes the running kernel for the availability of the specified map type. // // See the package documentation for the meaning of the error return value. func HaveMapType(mt ebpf.MapType) error { return haveMapTypeMatrix.Result(mt) } func probeCgroupStorageMap(mt sys.MapType) error { // keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16) // by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs return createMap(&sys.MapCreateAttr{ MapType: mt, ValueSize: 4, KeySize: uint32(8 + unsafe.Sizeof(int(0))), MaxEntries: 0, }) } func probeStorageMap(mt sys.MapType) error { // maxEntries needs to be 0 // BPF_F_NO_PREALLOC needs to be set // btf* fields need to be set // see alloc_check for local_storage map types err := createMap(&sys.MapCreateAttr{ MapType: mt, KeySize: 4, ValueSize: 4, MaxEntries: 0, MapFlags: sys.BPF_F_NO_PREALLOC, BtfKeyTypeId: 1, BtfValueTypeId: 1, BtfFd: ^uint32(0), }) if errors.Is(err, unix.EBADF) { // Triggered by BtfFd. return nil } return err } func probeNestedMap(mt sys.MapType) error { // assign invalid innerMapFd to pass validation check // will return EBADF err := probeMap(&sys.MapCreateAttr{ MapType: mt, InnerMapFd: ^uint32(0), }) if errors.Is(err, unix.EBADF) { return nil } return err } func probeMap(attr *sys.MapCreateAttr) error { if attr.KeySize == 0 { attr.KeySize = 4 } if attr.ValueSize == 0 { attr.ValueSize = 4 } attr.MaxEntries = 1 return createMap(attr) } func createMap(attr *sys.MapCreateAttr) error { fd, err := sys.MapCreate(attr) if err == nil { fd.Close() return nil } switch { // EINVAL occurs when attempting to create a map with an unknown type. // E2BIG occurs when MapCreateAttr contains non-zero bytes past the end // of the struct known by the running kernel, meaning the kernel is too old // to support the given map type. case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): return ebpf.ErrNotSupported } return err } var haveMapTypeMatrix = internal.FeatureMatrix[ebpf.MapType]{ ebpf.Hash: {Version: "3.19"}, ebpf.Array: {Version: "3.19"}, ebpf.ProgramArray: {Version: "4.2"}, ebpf.PerfEventArray: {Version: "4.3"}, ebpf.PerCPUHash: {Version: "4.6"}, ebpf.PerCPUArray: {Version: "4.6"}, ebpf.StackTrace: { Version: "4.6", Fn: func() error { return probeMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_STACK_TRACE, ValueSize: 8, // sizeof(uint64) }) }, }, ebpf.CGroupArray: {Version: "4.8"}, ebpf.LRUHash: {Version: "4.10"}, ebpf.LRUCPUHash: {Version: "4.10"}, ebpf.LPMTrie: { Version: "4.11", Fn: func() error { // keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8 // BPF_F_NO_PREALLOC needs to be set return probeMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_LPM_TRIE, KeySize: 8, ValueSize: 8, MapFlags: sys.BPF_F_NO_PREALLOC, }) }, }, ebpf.ArrayOfMaps: { Version: "4.12", Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_ARRAY_OF_MAPS) }, }, ebpf.HashOfMaps: { Version: "4.12", Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_HASH_OF_MAPS) }, }, ebpf.DevMap: {Version: "4.14"}, ebpf.SockMap: {Version: "4.14"}, ebpf.CPUMap: {Version: "4.15"}, ebpf.XSKMap: {Version: "4.18"}, ebpf.SockHash: {Version: "4.18"}, ebpf.CGroupStorage: { Version: "4.19", Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_CGROUP_STORAGE) }, }, ebpf.ReusePortSockArray: {Version: "4.19"}, ebpf.PerCPUCGroupStorage: { Version: "4.20", Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) }, }, ebpf.Queue: { Version: "4.20", Fn: func() error { return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_QUEUE, KeySize: 0, ValueSize: 4, MaxEntries: 1, }) }, }, ebpf.Stack: { Version: "4.20", Fn: func() error { return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_STACK, KeySize: 0, ValueSize: 4, MaxEntries: 1, }) }, }, ebpf.SkStorage: { Version: "5.2", Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_SK_STORAGE) }, }, ebpf.DevMapHash: {Version: "5.4"}, ebpf.StructOpsMap: { Version: "5.6", Fn: func() error { // StructOps requires setting a vmlinux type id, but id 1 will always // resolve to some type of integer. This will cause ENOTSUPP. err := probeMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_STRUCT_OPS, BtfVmlinuxValueTypeId: 1, }) if errors.Is(err, sys.ENOTSUPP) { // ENOTSUPP means the map type is at least known to the kernel. return nil } return err }, }, ebpf.RingBuf: { Version: "5.8", Fn: func() error { // keySize and valueSize need to be 0 // maxEntries needs to be power of 2 and PAGE_ALIGNED return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_RINGBUF, KeySize: 0, ValueSize: 0, MaxEntries: uint32(os.Getpagesize()), }) }, }, ebpf.InodeStorage: { Version: "5.10", Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_INODE_STORAGE) }, }, ebpf.TaskStorage: { Version: "5.11", Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_TASK_STORAGE) }, }, ebpf.BloomFilter: { Version: "5.16", Fn: func() error { return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_BLOOM_FILTER, KeySize: 0, ValueSize: 4, MaxEntries: 1, }) }, }, ebpf.UserRingbuf: { Version: "6.1", Fn: func() error { // keySize and valueSize need to be 0 // maxEntries needs to be power of 2 and PAGE_ALIGNED return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_USER_RINGBUF, KeySize: 0, ValueSize: 0, MaxEntries: uint32(os.Getpagesize()), }) }, }, ebpf.CgroupStorage: { Version: "6.2", Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_CGRP_STORAGE) }, }, ebpf.Arena: { Version: "6.9", Fn: func() error { return createMap(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_ARENA, KeySize: 0, ValueSize: 0, MaxEntries: 1, // one page MapExtra: 0, // can mmap() at any address MapFlags: sys.BPF_F_MMAPABLE, }) }, }, } func init() { for mt, ft := range haveMapTypeMatrix { ft.Name = mt.String() if ft.Fn == nil { // Avoid referring to the loop variable in the closure. mt := sys.MapType(mt) ft.Fn = func() error { return probeMap(&sys.MapCreateAttr{MapType: mt}) } } } } // MapFlags document which flags may be feature probed. type MapFlags uint32 // Flags which may be feature probed. const ( BPF_F_NO_PREALLOC = sys.BPF_F_NO_PREALLOC BPF_F_RDONLY_PROG = sys.BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG = sys.BPF_F_WRONLY_PROG BPF_F_MMAPABLE = sys.BPF_F_MMAPABLE BPF_F_INNER_MAP = sys.BPF_F_INNER_MAP ) // HaveMapFlag probes the running kernel for the availability of the specified map flag. // // Returns an error if flag is not one of the flags declared in this package. // See the package documentation for the meaning of the error return value. func HaveMapFlag(flag MapFlags) (err error) { return haveMapFlagsMatrix.Result(flag) } func probeMapFlag(attr *sys.MapCreateAttr) error { // For now, we do not check if the map type is supported because we only support // probing for flags defined on arrays and hashes that are always supported. // In the future, if we allow probing on flags defined on newer types, checking for map type // support will be required. if attr.MapType == sys.BPF_MAP_TYPE_UNSPEC { attr.MapType = sys.BPF_MAP_TYPE_ARRAY } attr.KeySize = 4 attr.ValueSize = 4 attr.MaxEntries = 1 fd, err := sys.MapCreate(attr) if err == nil { fd.Close() } else if errors.Is(err, unix.EINVAL) { // EINVAL occurs when attempting to create a map with an unknown type or an unknown flag. err = ebpf.ErrNotSupported } return err } var haveMapFlagsMatrix = internal.FeatureMatrix[MapFlags]{ BPF_F_NO_PREALLOC: { Version: "4.6", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapType: sys.BPF_MAP_TYPE_HASH, MapFlags: BPF_F_NO_PREALLOC, }) }, }, BPF_F_RDONLY_PROG: { Version: "5.2", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_RDONLY_PROG, }) }, }, BPF_F_WRONLY_PROG: { Version: "5.2", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_WRONLY_PROG, }) }, }, BPF_F_MMAPABLE: { Version: "5.5", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_MMAPABLE, }) }, }, BPF_F_INNER_MAP: { Version: "5.10", Fn: func() error { return probeMapFlag(&sys.MapCreateAttr{ MapFlags: BPF_F_INNER_MAP, }) }, }, } func init() { for mf, ft := range haveMapFlagsMatrix { ft.Name = fmt.Sprint(mf) } } golang-github-cilium-ebpf-0.21.0+ds1/features/map_test.go000066400000000000000000000011171520243672000231600ustar00rootroot00000000000000package features import ( "errors" "math" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveMapType(t *testing.T) { testutils.CheckFeatureMatrix(t, haveMapTypeMatrix) } func TestHaveMapFlag(t *testing.T) { testutils.CheckFeatureMatrix(t, haveMapFlagsMatrix) } func TestHaveMapTypeInvalid(t *testing.T) { if err := HaveMapType(ebpf.MapType(math.MaxUint32)); err == nil { t.Fatal("Expected an error") } else if errors.Is(err, internal.ErrNotSupported) { t.Fatal("Got ErrNotSupported:", err) } } golang-github-cilium-ebpf-0.21.0+ds1/features/misc.go000066400000000000000000000073031520243672000223020ustar00rootroot00000000000000package features import ( "errors" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) // HaveLargeInstructions probes the running kernel if more than 4096 instructions // per program are supported. // // Upstream commit c04c0d2b968a ("bpf: increase complexity limit and maximum program size"). // // See the package documentation for the meaning of the error return value. func HaveLargeInstructions() error { return haveLargeInstructions() } var haveLargeInstructions = internal.NewFeatureTest(">4096 instructions", func() error { const maxInsns = 4096 insns := make(asm.Instructions, maxInsns, maxInsns+1) for i := range insns { insns[i] = asm.Mov.Imm(asm.R0, 1) } insns = append(insns, asm.Return()) return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: insns, }) }, "5.2") // HaveBoundedLoops probes the running kernel if bounded loops are supported. // // Upstream commit 2589726d12a1 ("bpf: introduce bounded loops"). // // See the package documentation for the meaning of the error return value. func HaveBoundedLoops() error { return haveBoundedLoops() } var haveBoundedLoops = internal.NewFeatureTest("bounded loops", func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 10), asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"), asm.JNE.Imm(asm.R0, 0, "loop"), asm.Return(), }, }) }, "5.3") // HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported. // // Upstream commit 92b31a9af73b ("bpf: add BPF_J{LT,LE,SLT,SLE} instructions"). // // See the package documentation for the meaning of the error return value. func HaveV2ISA() error { return haveV2ISA() } var haveV2ISA = internal.NewFeatureTest("v2 ISA", func() error { err := probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.JLT.Imm(asm.R0, 0, "exit"), asm.Mov.Imm(asm.R0, 1), asm.Return().WithSymbol("exit"), }, }) // This sometimes bubbles up from the JIT on aarch64. if errors.Is(err, sys.ENOTSUPP) { return ebpf.ErrNotSupported } return err }, "4.14") // HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported. // // Upstream commit 092ed0968bb6 ("bpf: verifier support JMP32"). // // See the package documentation for the meaning of the error return value. func HaveV3ISA() error { return haveV3ISA() } var haveV3ISA = internal.NewFeatureTest("v3 ISA", func() error { err := probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.JLT.Imm32(asm.R0, 0, "exit"), asm.Mov.Imm(asm.R0, 1), asm.Return().WithSymbol("exit"), }, }) // This sometimes bubbles up from the JIT on aarch64. if errors.Is(err, sys.ENOTSUPP) { return ebpf.ErrNotSupported } return err }, "5.1") // HaveV4ISA probes the running kernel if instructions of the v4 ISA are supported. // // Upstream commit 1f9a1ea821ff ("bpf: Support new sign-extension load insns"). // // See the package documentation for the meaning of the error return value. func HaveV4ISA() error { return haveV4ISA() } var haveV4ISA = internal.NewFeatureTest("v4 ISA", func() error { err := probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SocketFilter, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.JEq.Imm(asm.R0, 1, "error"), asm.LongJump("exit"), asm.Mov.Imm(asm.R0, 1).WithSymbol("error"), asm.Return().WithSymbol("exit"), }, }) // This sometimes bubbles up from the JIT on aarch64. if errors.Is(err, sys.ENOTSUPP) { return ebpf.ErrNotSupported } return err }, "6.6") golang-github-cilium-ebpf-0.21.0+ds1/features/misc_test.go000066400000000000000000000010141520243672000233320ustar00rootroot00000000000000package features import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveLargeInstructions(t *testing.T) { testutils.CheckFeatureTest(t, HaveLargeInstructions) } func TestHaveBoundedLoops(t *testing.T) { testutils.CheckFeatureTest(t, HaveBoundedLoops) } func TestHaveV2ISA(t *testing.T) { testutils.CheckFeatureTest(t, HaveV2ISA) } func TestHaveV3ISA(t *testing.T) { testutils.CheckFeatureTest(t, HaveV3ISA) } func TestHaveV4ISA(t *testing.T) { testutils.CheckFeatureTest(t, HaveV4ISA) } golang-github-cilium-ebpf-0.21.0+ds1/features/prog.go000066400000000000000000000215651520243672000223240ustar00rootroot00000000000000package features import ( "errors" "fmt" "slices" "strings" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // HaveProgramType probes the running kernel for the availability of the specified program type. // // See the package documentation for the meaning of the error return value. func HaveProgramType(pt ebpf.ProgramType) (err error) { return haveProgramTypeMatrix.Result(pt) } func probeProgram(spec *ebpf.ProgramSpec) error { if spec.Instructions == nil { spec.Instructions = asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), } } prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ LogDisabled: true, }) if err == nil { prog.Close() } switch { // EINVAL occurs when attempting to create a program with an unknown type. // E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end // of the struct known by the running kernel, meaning the kernel is too old // to support the given prog type. case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): err = ebpf.ErrNotSupported } return err } var haveProgramTypeMatrix = internal.FeatureMatrix[ebpf.ProgramType]{ ebpf.SocketFilter: {Version: "3.19"}, ebpf.Kprobe: {Version: "4.1"}, ebpf.SchedCLS: {Version: "4.1"}, ebpf.SchedACT: {Version: "4.1"}, ebpf.TracePoint: {Version: "4.7"}, ebpf.XDP: {Version: "4.8"}, ebpf.PerfEvent: {Version: "4.9"}, ebpf.CGroupSKB: {Version: "4.10"}, ebpf.CGroupSock: {Version: "4.10"}, ebpf.LWTIn: {Version: "4.10"}, ebpf.LWTOut: {Version: "4.10"}, ebpf.LWTXmit: {Version: "4.10"}, ebpf.SockOps: {Version: "4.13"}, ebpf.SkSKB: {Version: "4.14"}, ebpf.CGroupDevice: {Version: "4.15"}, ebpf.SkMsg: {Version: "4.17"}, ebpf.RawTracepoint: {Version: "4.17"}, ebpf.CGroupSockAddr: { Version: "4.17", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSockAddr, AttachType: ebpf.AttachCGroupInet4Connect, }) }, }, ebpf.LWTSeg6Local: {Version: "4.18"}, ebpf.LircMode2: {Version: "4.18"}, ebpf.SkReuseport: {Version: "4.19"}, ebpf.FlowDissector: {Version: "4.20"}, ebpf.CGroupSysctl: {Version: "5.2"}, ebpf.RawTracepointWritable: {Version: "5.2"}, ebpf.CGroupSockopt: { Version: "5.3", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSockopt, AttachType: ebpf.AttachCGroupGetsockopt, }) }, }, ebpf.Tracing: { Version: "5.5", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.Tracing, AttachType: ebpf.AttachTraceFEntry, AttachTo: "bpf_init", }) }, }, ebpf.StructOps: { Version: "5.6", Fn: func() error { err := probeProgram(&ebpf.ProgramSpec{ Type: ebpf.StructOps, License: "GPL", }) if errors.Is(err, sys.ENOTSUPP) { // ENOTSUPP means the program type is at least known to the kernel. return nil } return err }, }, ebpf.Extension: { Version: "5.6", Fn: func() error { // create btf.Func to add to first ins of target and extension so both progs are btf powered btfFn := btf.Func{ Name: "a", Type: &btf.FuncProto{ Return: &btf.Int{}, Params: []btf.FuncParam{ {Name: "ctx", Type: &btf.Pointer{Target: &btf.Struct{Name: "xdp_md"}}}, }, }, Linkage: btf.GlobalFunc, } insns := asm.Instructions{ btf.WithFuncMetadata(asm.Mov.Imm(asm.R0, 0), &btfFn), asm.Return(), } // create target prog prog, err := ebpf.NewProgramWithOptions( &ebpf.ProgramSpec{ Type: ebpf.XDP, Instructions: insns, }, ebpf.ProgramOptions{ LogDisabled: true, }, ) if err != nil { return err } defer prog.Close() // probe for Extension prog with target return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.Extension, Instructions: insns, AttachTarget: prog, AttachTo: btfFn.Name, }) }, }, ebpf.LSM: { Version: "5.7", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.LSM, AttachType: ebpf.AttachLSMMac, AttachTo: "file_mprotect", License: "GPL", }) }, }, ebpf.SkLookup: { Version: "5.9", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.SkLookup, AttachType: ebpf.AttachSkLookup, }) }, }, ebpf.Syscall: { Version: "5.14", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.Syscall, Flags: sys.BPF_F_SLEEPABLE, }) }, }, ebpf.Netfilter: { Version: "6.4", Fn: func() error { return probeProgram(&ebpf.ProgramSpec{ Type: ebpf.Netfilter, AttachType: ebpf.AttachNetfilter, }) }, }, } func init() { for key, ft := range haveProgramTypeMatrix { ft.Name = key.String() if ft.Fn == nil { key := key // avoid the dreaded loop variable problem ft.Fn = func() error { return probeProgram(&ebpf.ProgramSpec{Type: key}) } } } } type helperKey struct { typ ebpf.ProgramType helper asm.BuiltinFunc } var helperCache = internal.NewFeatureCache(func(key helperKey) *internal.FeatureTest { return &internal.FeatureTest{ Name: fmt.Sprintf("%s for program type %s", key.helper, key.typ), Fn: func() error { return haveProgramHelper(key.typ, key.helper) }, } }) // HaveProgramHelper probes the running kernel for the availability of the specified helper // function to a specified program type. // Return values have the following semantics: // // err == nil: The feature is available. // errors.Is(err, ebpf.ErrNotSupported): The feature is not available. // err != nil: Any errors encountered during probe execution, wrapped. // // Note that the latter case may include false negatives, and that program creation may // succeed despite an error being returned. // Only `nil` and `ebpf.ErrNotSupported` are conclusive. // // Probe results are cached and persist throughout any process capability changes. func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { return helperCache.Result(helperKey{pt, helper}) } func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { if ok := helperProbeNotImplemented(pt); ok { return fmt.Errorf("no feature probe for %v/%v", pt, helper) } if err := HaveProgramType(pt); err != nil { return err } spec := &ebpf.ProgramSpec{ Type: pt, Instructions: asm.Instructions{ helper.Call(), asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "GPL", } switch pt { case ebpf.CGroupSockAddr: spec.AttachType = ebpf.AttachCGroupInet4Connect case ebpf.CGroupSockopt: spec.AttachType = ebpf.AttachCGroupGetsockopt case ebpf.SkLookup: spec.AttachType = ebpf.AttachSkLookup case ebpf.Syscall: spec.Flags = sys.BPF_F_SLEEPABLE case ebpf.Netfilter: spec.AttachType = ebpf.AttachNetfilter } prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ LogLevel: 1, }) if err == nil { prog.Close() } var verr *ebpf.VerifierError if !errors.As(err, &verr) { return err } helperTag := fmt.Sprintf("#%d", helper) switch { // EACCES occurs when attempting to create a program probe with a helper // while the register args when calling this helper aren't set up properly. // We interpret this as the helper being available, because the verifier // returns EINVAL if the helper is not supported by the running kernel. case errors.Is(err, unix.EACCES): err = nil // EINVAL occurs when attempting to create a program with an unknown helper. case errors.Is(err, unix.EINVAL): // https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10663 if logContainsAll(verr.Log, "invalid func", helperTag) { return ebpf.ErrNotSupported } // https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10668 wrongProgramType := logContainsAll(verr.Log, "program of this type cannot use helper", helperTag) // https://github.com/torvalds/linux/blob/59b418c7063d30e0a3e1f592d47df096db83185c/kernel/bpf/verifier.c#L10204 // 4.9 doesn't include # in verifier output. wrongProgramType = wrongProgramType || logContainsAll(verr.Log, "unknown func") if wrongProgramType { return fmt.Errorf("program of this type cannot use helper: %w", ebpf.ErrNotSupported) } } return err } func logContainsAll(log []string, needles ...string) bool { first := max(len(log)-5, 0) // Check last 5 lines. return slices.ContainsFunc(log[first:], func(line string) bool { for _, needle := range needles { if !strings.Contains(line, needle) { return false } } return true }) } func helperProbeNotImplemented(pt ebpf.ProgramType) bool { switch pt { case ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing: return true } return false } golang-github-cilium-ebpf-0.21.0+ds1/features/prog_test.go000066400000000000000000000103511520243672000233520ustar00rootroot00000000000000package features import ( "errors" "fmt" "math" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/testmain" ) func TestMain(m *testing.M) { testmain.Run(m) } func TestHaveProgramType(t *testing.T) { testutils.CheckFeatureMatrix(t, haveProgramTypeMatrix) } func TestHaveProgramTypeInvalid(t *testing.T) { if err := HaveProgramType(ebpf.ProgramType(math.MaxUint32)); err == nil { t.Fatal("Expected an error") } else if errors.Is(err, internal.ErrNotSupported) { t.Fatal("Got ErrNotSupported:", err) } } func TestHaveProgramHelper(t *testing.T) { type testCase struct { prog ebpf.ProgramType helper asm.BuiltinFunc expected error version string } // Referencing linux kernel commits to track the kernel version required to pass these test cases. // These cases are derived from libbpf's selftests and helper/prog combinations that are // probed for in cilium/cilium. testCases := []testCase{ {ebpf.Kprobe, asm.FnMapLookupElem, nil, "3.19"}, // d0003ec01c66 {ebpf.SocketFilter, asm.FnKtimeGetCoarseNs, nil, "5.11"}, // d05512618056 {ebpf.SchedCLS, asm.FnSkbVlanPush, nil, "4.3"}, // 4e10df9a60d9 {ebpf.Kprobe, asm.FnSkbVlanPush, ebpf.ErrNotSupported, "4.3"}, // 4e10df9a60d9 {ebpf.Kprobe, asm.FnSysBpf, ebpf.ErrNotSupported, "5.14"}, // 79a7f8bdb159 {ebpf.Syscall, asm.FnSysBpf, nil, "5.14"}, // 79a7f8bdb159 {ebpf.XDP, asm.FnJiffies64, nil, "5.5"}, // 5576b991e9c1 {ebpf.XDP, asm.FnKtimeGetBootNs, nil, "5.7"}, // 71d19214776e {ebpf.SchedCLS, asm.FnSkbChangeHead, nil, "5.8"}, // 6f3f65d80dac {ebpf.SchedCLS, asm.FnRedirectNeigh, nil, "5.10"}, // b4ab31414970 {ebpf.SchedCLS, asm.FnSkbEcnSetCe, nil, "5.1"}, // f7c917ba11a6 {ebpf.SchedACT, asm.FnSkAssign, nil, "5.6"}, // cf7fbe660f2d {ebpf.SchedACT, asm.FnFibLookup, nil, "4.18"}, // 87f5fc7e48dd {ebpf.Kprobe, asm.FnFibLookup, ebpf.ErrNotSupported, "4.18"}, // 87f5fc7e48dd {ebpf.CGroupSockAddr, asm.FnGetsockopt, nil, "5.8"}, // beecf11bc218 {ebpf.CGroupSockAddr, asm.FnSkLookupTcp, nil, "4.20"}, // 6acc9b432e67 {ebpf.CGroupSockAddr, asm.FnGetNetnsCookie, nil, "5.7"}, // f318903c0bf4 {ebpf.CGroupSock, asm.FnGetNetnsCookie, nil, "5.7"}, // f318903c0bf4 {ebpf.Kprobe, asm.FnKtimeGetCoarseNs, ebpf.ErrNotSupported, "5.16"}, // 5e0bc3082e2e {ebpf.CGroupSockAddr, asm.FnGetCgroupClassid, nil, "5.7"}, // 5a52ae4e32a6 {ebpf.Kprobe, asm.FnGetBranchSnapshot, nil, "5.16"}, // 856c02dbce4f {ebpf.SchedCLS, asm.FnSkbSetTstamp, nil, "5.18"}, // 9bb984f28d5b {ebpf.CGroupSockopt, asm.FnSkStorageDelete, nil, "5.3"}, // 6ac99e8f23d4 {ebpf.SkLookup, asm.FnSkcToUdp6Sock, nil, "5.9"}, // 0d4fad3e57df {ebpf.Syscall, asm.FnSysClose, nil, "5.14"}, // 3abea089246f {ebpf.Netfilter, asm.FnCgrpStorageDelete, nil, "6.4"}, // c4bcfb38a95e } for _, tc := range testCases { t.Run(fmt.Sprintf("%s/%s", tc.prog.String(), tc.helper.String()), func(t *testing.T) { feature := fmt.Sprintf("helper %s for program type %s", tc.helper.String(), tc.prog.String()) testutils.SkipOnOldKernel(t, tc.version, feature) err := HaveProgramHelper(tc.prog, tc.helper) testutils.SkipIfNotSupportedOnOS(t, err) if !errors.Is(err, tc.expected) { t.Fatalf("%s/%s: %v", tc.prog.String(), tc.helper.String(), err) } }) } } func TestHelperProbeNotImplemented(t *testing.T) { // Currently we don't support probing helpers for Tracing, Extension, LSM and StructOps programs. // For each of those test the availability of the FnMapLookupElem helper and expect it to fail. for _, pt := range []ebpf.ProgramType{ebpf.Tracing, ebpf.Extension, ebpf.LSM, ebpf.StructOps} { t.Run(pt.String(), func(t *testing.T) { if err := HaveProgramHelper(pt, asm.FnMapLookupElem); err == nil { t.Fatal("Expected an error") } }) } } golang-github-cilium-ebpf-0.21.0+ds1/features/version.go000066400000000000000000000011701520243672000230300ustar00rootroot00000000000000package features import "github.com/cilium/ebpf/internal/linux" // LinuxVersionCode returns the version of the currently running kernel // as defined in the LINUX_VERSION_CODE compile-time macro. It is represented // in the format described by the KERNEL_VERSION macro from linux/version.h. // // Do not use the version to make assumptions about the presence of certain // kernel features, always prefer feature probes in this package. Some // distributions backport or disable eBPF features. func LinuxVersionCode() (uint32, error) { v, err := linux.KernelVersion() if err != nil { return 0, err } return v.Kernel(), nil } golang-github-cilium-ebpf-0.21.0+ds1/fuzz_test.go000066400000000000000000000007051520243672000215650ustar00rootroot00000000000000package ebpf import ( "bytes" "debug/elf" "testing" ) func FuzzLoadCollectionSpec(f *testing.F) { f.Add([]byte(elf.ELFMAG)) f.Fuzz(func(t *testing.T, data []byte) { if len(data) < len(elf.ELFMAG) { t.Skip("input can't be valid ELF") } spec, err := LoadCollectionSpecFromReader(bytes.NewReader(data)) if err != nil { if spec != nil { t.Fatal("spec is not nil") } } else if spec == nil { t.Fatal("spec is nil") } }) } golang-github-cilium-ebpf-0.21.0+ds1/go.mod000066400000000000000000000034601520243672000203100ustar00rootroot00000000000000module github.com/cilium/ebpf go 1.24.0 require ( github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 github.com/google/go-cmp v0.7.0 github.com/jsimonetti/rtnetlink/v2 v2.0.1 golang.org/x/sync v0.17.0 golang.org/x/sys v0.37.0 ) require ( github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/cli v29.2.0+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/google/go-containerregistry v0.20.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.7 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/vbatts/tar-split v0.12.1 // indirect golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.46.0 // indirect golang.org/x/tools v0.38.0 // indirect gotest.tools/v3 v3.5.0 // indirect ) tool ( github.com/cilium/ebpf/cmd/bpf2go github.com/cilium/ebpf/internal/cmd/gentypes github.com/google/go-containerregistry/cmd/crane golang.org/x/tools/cmd/stringer ) golang-github-cilium-ebpf-0.21.0+ds1/go.sum000066400000000000000000000161431520243672000203370ustar00rootroot00000000000000github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM= github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= golang-github-cilium-ebpf-0.21.0+ds1/helpers_test.go000066400000000000000000000170501520243672000222320ustar00rootroot00000000000000package ebpf import ( "errors" "sync" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/testutils" ) var haveTestmod = sync.OnceValues(func() (bool, error) { if platform.IsWindows { return false, nil } // See https://github.com/torvalds/linux/commit/290248a5b7d829871b3ea3c62578613a580a1744 testmod, err := btf.FindHandle(func(info *btf.HandleInfo) bool { return info.IsModule() && info.Name == "bpf_testmod" }) if err != nil && !errors.Is(err, btf.ErrNotFound) { return false, err } testmod.Close() return testmod != nil, nil }) var haveTestmodOps = sync.OnceValues(func() (bool, error) { haveTestMod, err := haveTestmod() if err != nil { return false, err } if !haveTestMod { return false, nil } target := btf.Type((*btf.Struct)(nil)) _, module, err := findTargetInKernel("bpf_struct_ops_bpf_testmod_ops", &target, btf.NewCache()) if err != nil && !errors.Is(err, btf.ErrNotFound) { return false, err } if errors.Is(err, btf.ErrNotFound) { return false, nil } defer module.Close() return true, nil }) func requireTestmod(tb testing.TB) { tb.Helper() testutils.SkipOnOldKernel(tb, "5.11", "bpf_testmod") testmod, err := haveTestmod() if err != nil { tb.Fatal(err) } if !testmod { tb.Skip("bpf_testmod not loaded") } } func requireTestmodOps(tb testing.TB) { tb.Helper() testutils.SkipOnOldKernel(tb, "5.11", "bpf_testmod") testmodOps, err := haveTestmodOps() if err != nil { tb.Fatal(err) } if !testmodOps { tb.Skip("bpf_testmod_ops not loaded") } } func newMap(tb testing.TB, spec *MapSpec, opts *MapOptions) (*Map, error) { tb.Helper() spec = fixupMapSpec(spec) if opts == nil { opts = new(MapOptions) } m, err := NewMapWithOptions(spec, *opts) testutils.SkipIfNotSupportedOnOS(tb, err) if err != nil { return nil, err } tb.Cleanup(func() { m.Close() }) return m, nil } func mustNewMap(tb testing.TB, spec *MapSpec, opts *MapOptions) *Map { tb.Helper() m, err := newMap(tb, spec, opts) qt.Assert(tb, qt.IsNil(err)) return m } func createMap(tb testing.TB, typ MapType, maxEntries uint32) *Map { tb.Helper() return mustNewMap(tb, &MapSpec{ Name: "test", Type: typ, KeySize: 4, ValueSize: 4, MaxEntries: maxEntries, }, nil) } func createMapInMap(tb testing.TB, outer, inner MapType) *Map { tb.Helper() return mustNewMap(tb, &MapSpec{ Type: outer, KeySize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Type: inner, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, }, nil) } func newProgram(tb testing.TB, spec *ProgramSpec, opts *ProgramOptions) (*Program, error) { tb.Helper() if opts == nil { opts = new(ProgramOptions) } spec = fixupProgramSpec(spec) prog, err := NewProgramWithOptions(spec, *opts) testutils.SkipIfNotSupportedOnOS(tb, err) if err != nil { return nil, err } tb.Cleanup(func() { prog.Close() }) return prog, nil } func mustNewProgram(tb testing.TB, spec *ProgramSpec, opts *ProgramOptions) *Program { tb.Helper() prog, err := newProgram(tb, spec, opts) qt.Assert(tb, qt.IsNil(err)) return prog } func createProgram(tb testing.TB, typ ProgramType, retval int64) *Program { tb.Helper() return mustNewProgram(tb, &ProgramSpec{ Name: "test", Type: typ, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, retval, asm.DWord), asm.Return(), }, License: "MIT", }, nil) } var basicProgramSpec = &ProgramSpec{ Name: "test", Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 2, asm.DWord), asm.Return(), }, License: "MIT", } // createBasicProgram returns a program of an unspecified type which returns // a non-zero value when executed. func createBasicProgram(tb testing.TB) *Program { return mustNewProgram(tb, basicProgramSpec, nil) } func newCollection(tb testing.TB, spec *CollectionSpec, opts *CollectionOptions) (*Collection, error) { tb.Helper() testutils.SkipNonNativeEndian(tb, spec.ByteOrder) spec = fixupCollectionSpec(spec) if opts == nil { opts = new(CollectionOptions) } c, err := NewCollectionWithOptions(spec, *opts) testutils.SkipIfNotSupportedOnOS(tb, err) if err != nil { return nil, err } tb.Cleanup(func() { c.Close() }) return c, nil } func mustNewCollection(tb testing.TB, spec *CollectionSpec, opts *CollectionOptions) *Collection { tb.Helper() c, err := newCollection(tb, spec, opts) qt.Assert(tb, qt.IsNil(err)) return c } func loadAndAssign(tb testing.TB, spec *CollectionSpec, to any, opts *CollectionOptions) error { tb.Helper() spec = fixupCollectionSpec(spec) err := spec.LoadAndAssign(to, opts) testutils.SkipIfNotSupported(tb, err) return err } func mustLoadAndAssign(tb testing.TB, spec *CollectionSpec, to any, opts *CollectionOptions) { qt.Assert(tb, qt.IsNil(loadAndAssign(tb, spec, to, opts))) } func mustRun(tb testing.TB, prog *Program, opts *RunOptions) (retval uint32) { tb.Helper() if opts == nil { opts = &RunOptions{} } if platform.IsLinux && opts.Data == nil { opts.Data = internal.EmptyBPFContext } if platform.IsWindows { switch prog.Type() { case WindowsSample: const minSampleContextLen = 32 if opts.Context == nil { opts.Context = make([]byte, minSampleContextLen) } } } ret, err := prog.Run(opts) testutils.SkipIfNotSupported(tb, err) qt.Assert(tb, qt.IsNil(err)) return ret } // The functions below translate Linux types to their Windows equivalents, if // possible. This allows running most tests on Windows without modification. func fixupMapType(typ MapType) MapType { if !platform.IsWindows { return typ } switch typ { case Array: return WindowsArray case Hash: return WindowsHash case ProgramArray: return WindowsProgramArray case PerCPUHash: return WindowsPerCPUHash case PerCPUArray: return WindowsPerCPUArray case LRUHash: return WindowsLRUHash case LRUCPUHash: return WindowsLRUCPUHash case ArrayOfMaps: return WindowsArrayOfMaps case HashOfMaps: return WindowsHashOfMaps case LPMTrie: return WindowsLPMTrie case Queue: return WindowsQueue case Stack: return WindowsStack case RingBuf: return WindowsRingBuf default: return typ } } func fixupMapSpec(spec *MapSpec) *MapSpec { if !platform.IsWindows { return spec } spec = spec.Copy() spec.Type = fixupMapType(spec.Type) if spec.InnerMap != nil { spec.InnerMap.Type = fixupMapType(spec.InnerMap.Type) } return spec } func fixupProgramType(typ ProgramType) ProgramType { if !platform.IsWindows { return typ } switch typ { case SocketFilter: return WindowsSample case XDP: return WindowsSample default: return typ } } func fixupProgramSpec(spec *ProgramSpec) *ProgramSpec { if !platform.IsWindows { return spec } spec = spec.Copy() spec.Type = fixupProgramType(spec.Type) for i, ins := range spec.Instructions { if ins.IsBuiltinCall() { switch asm.BuiltinFunc(ins.Constant) { case asm.FnMapUpdateElem: spec.Instructions[i].Constant = int64(asm.WindowsFnMapUpdateElem) case asm.FnMapLookupElem: spec.Instructions[i].Constant = int64(asm.WindowsFnMapLookupElem) case asm.FnTailCall: spec.Instructions[i].Constant = int64(asm.WindowsFnTailCall) } } } return spec } func fixupCollectionSpec(spec *CollectionSpec) *CollectionSpec { if !platform.IsWindows { return spec } spec = spec.Copy() for name := range spec.Maps { spec.Maps[name] = fixupMapSpec(spec.Maps[name]) } for name := range spec.Programs { spec.Programs[name] = fixupProgramSpec(spec.Programs[name]) } return spec } golang-github-cilium-ebpf-0.21.0+ds1/info.go000066400000000000000000000726311520243672000204720ustar00rootroot00000000000000package ebpf import ( "bufio" "bytes" "encoding/hex" "errors" "fmt" "io" "os" "reflect" "time" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // The *Info structs expose metadata about a program or map. Most // fields are exposed via a getter: // // func (*MapInfo) ID() (MapID, bool) // // This is because the metadata available changes based on kernel version. // The second boolean return value indicates whether a particular field is // available on the current kernel. // // Always add new metadata as such a getter, unless you can somehow get the // value of the field on all supported kernels. Also document which version // a particular field first appeared in. // // Some metadata is a buffer which needs additional parsing. In this case, // store the undecoded data in the Info struct and provide a getter which // decodes it when necessary. See ProgramInfo.Instructions for an example. // MapInfo describes a map. type MapInfo struct { // Type of the map. Type MapType // KeySize is the size of the map key in bytes. KeySize uint32 // ValueSize is the size of the map value in bytes. ValueSize uint32 // MaxEntries is the maximum number of entries the map can hold. Its meaning // is map-specific. MaxEntries uint32 // Flags used during map creation. Flags uint32 // Name as supplied by user space at load time. Available from 4.15. Name string id MapID btf btf.ID mapExtra uint64 memlock uint64 frozen bool } // minimalMapInfoFromFd queries the minimum information needed to create a Map // based on a file descriptor. This requires the map type, key/value sizes, // maxentries and flags. // // Does not fall back to fdinfo since the version gap between fdinfo (4.10) and // [sys.ObjInfo] (4.13) is small and both kernels are EOL since at least Nov // 2017. // // Requires at least Linux 4.13. func minimalMapInfoFromFd(fd *sys.FD) (*MapInfo, error) { var info sys.MapInfo if err := sys.ObjInfo(fd, &info); err != nil { return nil, fmt.Errorf("getting object info: %w", err) } typ, err := MapTypeForPlatform(platform.Native, info.Type) if err != nil { return nil, fmt.Errorf("map type: %w", err) } return &MapInfo{ Type: typ, KeySize: info.KeySize, ValueSize: info.ValueSize, MaxEntries: info.MaxEntries, Flags: uint32(info.MapFlags), Name: unix.ByteSliceToString(info.Name[:]), }, nil } // newMapInfoFromFd queries map information about the given fd. [sys.ObjInfo] is // attempted first, supplementing any missing values with information from // /proc/self/fdinfo. Ignores EINVAL from ObjInfo as well as ErrNotSupported // from reading fdinfo (indicating the file exists, but no fields of interest // were found). If both fail, an error is always returned. func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) { var info sys.MapInfo err1 := sys.ObjInfo(fd, &info) // EINVAL means the kernel doesn't support BPF_OBJ_GET_INFO_BY_FD. Continue // with fdinfo if that's the case. if err1 != nil && !errors.Is(err1, unix.EINVAL) { return nil, fmt.Errorf("getting object info: %w", err1) } typ, err := MapTypeForPlatform(platform.Native, info.Type) if err != nil { return nil, fmt.Errorf("map type: %w", err) } mi := &MapInfo{ typ, info.KeySize, info.ValueSize, info.MaxEntries, uint32(info.MapFlags), unix.ByteSliceToString(info.Name[:]), MapID(info.Id), btf.ID(info.BtfId), info.MapExtra, 0, false, } // Supplement OBJ_INFO with data from /proc/self/fdinfo. It contains fields // like memlock and frozen that are not present in OBJ_INFO. err2 := readMapInfoFromProc(fd, mi) if err2 != nil && !errors.Is(err2, ErrNotSupported) { return nil, fmt.Errorf("getting map info from fdinfo: %w", err2) } if err1 != nil && err2 != nil { return nil, fmt.Errorf("ObjInfo and fdinfo both failed: objinfo: %w, fdinfo: %w", err1, err2) } return mi, nil } // readMapInfoFromProc queries map information about the given fd from // /proc/self/fdinfo. It only writes data into fields that have a zero value. func readMapInfoFromProc(fd *sys.FD, mi *MapInfo) error { var mapType uint32 err := scanFdInfo(fd, map[string]interface{}{ "map_type": &mapType, "map_id": &mi.id, "key_size": &mi.KeySize, "value_size": &mi.ValueSize, "max_entries": &mi.MaxEntries, "map_flags": &mi.Flags, "map_extra": &mi.mapExtra, "memlock": &mi.memlock, "frozen": &mi.frozen, }) if err != nil { return err } if mi.Type == 0 { mi.Type, err = MapTypeForPlatform(platform.Linux, mapType) if err != nil { return fmt.Errorf("map type: %w", err) } } return nil } // ID returns the map ID. // // Available from 4.13. // // The bool return value indicates whether this optional field is available. func (mi *MapInfo) ID() (MapID, bool) { return mi.id, mi.id > 0 } // BTFID returns the BTF ID associated with the Map. // // The ID is only valid as long as the associated Map is kept alive. // Available from 4.18. // // The bool return value indicates whether this optional field is available and // populated. (The field may be available but not populated if the kernel // supports the field but the Map was loaded without BTF information.) func (mi *MapInfo) BTFID() (btf.ID, bool) { return mi.btf, mi.btf > 0 } // MapExtra returns an opaque field whose meaning is map-specific. // // Available from 5.16. // // The bool return value indicates whether this optional field is available and // populated, if it was specified during Map creation. func (mi *MapInfo) MapExtra() (uint64, bool) { return mi.mapExtra, mi.mapExtra > 0 } // Memlock returns an approximate number of bytes allocated to this map. // // Available from 4.10. // // The bool return value indicates whether this optional field is available. func (mi *MapInfo) Memlock() (uint64, bool) { return mi.memlock, mi.memlock > 0 } // Frozen indicates whether [Map.Freeze] was called on this map. If true, // modifications from user space are not allowed. // // Available from 5.2. Requires access to procfs. // // If the kernel doesn't support map freezing, this field will always be false. func (mi *MapInfo) Frozen() bool { return mi.frozen } // ProgramStats contains runtime statistics for a single [Program], returned by // [Program.Stats]. // // Will contain mostly zero values if the collection of statistics is not // enabled, see [EnableStats]. type ProgramStats struct { // Total accumulated runtime of the Program. // // Requires at least Linux 5.8. Runtime time.Duration // Total number of times the Program has executed. // // Requires at least Linux 5.8. RunCount uint64 // Total number of times the program was not executed due to recursion. This // can happen when another bpf program is already running on the cpu, when bpf // program execution is interrupted, for example. // // Requires at least Linux 5.12. RecursionMisses uint64 } func newProgramStatsFromFd(fd *sys.FD) (*ProgramStats, error) { var info sys.ProgInfo if err := sys.ObjInfo(fd, &info); err != nil { return nil, fmt.Errorf("getting program info: %w", err) } return &ProgramStats{ Runtime: time.Duration(info.RunTimeNs), RunCount: info.RunCnt, RecursionMisses: info.RecursionMisses, }, nil } // programJitedInfo holds information about JITed info of a program. type programJitedInfo struct { // ksyms holds the ksym addresses of the BPF program, including those of its // subprograms. // // Available from 4.18. ksyms []uint64 numKsyms uint32 // insns holds the JITed machine native instructions of the program, // including those of its subprograms. // // Available from 4.13. insns []byte numInsns uint32 // lineInfos holds the JITed line infos, which are kernel addresses. // // Available from 5.0. lineInfos []uint64 numLineInfos uint32 // lineInfoRecSize is the size of a single line info record. // // Available from 5.0. lineInfoRecSize uint32 // funcLens holds the insns length of each function. // // Available from 4.18. funcLens []uint32 numFuncLens uint32 } // ProgramInfo describes a Program's immutable metadata. For runtime statistics, // see [ProgramStats]. type ProgramInfo struct { Type ProgramType id ProgramID // Truncated hash of the BPF bytecode. Available from 4.13. Tag string // Name as supplied by user space at load time. Available from 4.15. Name string createdByUID uint32 haveCreatedByUID bool btf btf.ID loadTime time.Duration restricted bool maps []MapID insns []byte numInsns uint32 jitedSize uint32 verifiedInstructions uint32 jitedInfo programJitedInfo lineInfos []byte numLineInfos uint32 funcInfos []byte numFuncInfos uint32 memlock uint64 } // minimalProgramFromFd queries the minimum information needed to create a // Program based on a file descriptor, requiring at least the program type. // // Does not fall back to fdinfo since the version gap between fdinfo (4.10) and // [sys.ObjInfo] (4.13) is small and both kernels are EOL since at least Nov // 2017. // // Requires at least Linux 4.13. func minimalProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { var info sys.ProgInfo if err := sys.ObjInfo(fd, &info); err != nil { return nil, fmt.Errorf("getting object info: %w", err) } typ, err := ProgramTypeForPlatform(platform.Native, info.Type) if err != nil { return nil, fmt.Errorf("program type: %w", err) } return &ProgramInfo{ Type: typ, Name: unix.ByteSliceToString(info.Name[:]), }, nil } // newProgramInfoFromFd queries program information about the given fd. // // [sys.ObjInfo] is attempted first, supplementing any missing values with // information from /proc/self/fdinfo. Ignores EINVAL from ObjInfo as well as // ErrNotSupported from reading fdinfo (indicating the file exists, but no // fields of interest were found). If both fail, an error is always returned. func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { var info sys.ProgInfo err1 := sys.ObjInfo(fd, &info) // EINVAL means the kernel doesn't support BPF_OBJ_GET_INFO_BY_FD. Continue // with fdinfo if that's the case. if err1 != nil && !errors.Is(err1, unix.EINVAL) { return nil, fmt.Errorf("getting object info: %w", err1) } typ, err := ProgramTypeForPlatform(platform.Native, info.Type) if err != nil { return nil, fmt.Errorf("program type: %w", err) } pi := ProgramInfo{ Type: typ, id: ProgramID(info.Id), Tag: hex.EncodeToString(info.Tag[:]), Name: unix.ByteSliceToString(info.Name[:]), btf: btf.ID(info.BtfId), jitedSize: info.JitedProgLen, loadTime: time.Duration(info.LoadTime), verifiedInstructions: info.VerifiedInsns, numInsns: info.XlatedProgLen, } // Supplement OBJ_INFO with data from /proc/self/fdinfo. It contains fields // like memlock that is not present in OBJ_INFO. err2 := readProgramInfoFromProc(fd, &pi) if err2 != nil && !errors.Is(err2, ErrNotSupported) { return nil, fmt.Errorf("getting map info from fdinfo: %w", err2) } if err1 != nil && err2 != nil { return nil, fmt.Errorf("ObjInfo and fdinfo both failed: objinfo: %w, fdinfo: %w", err1, err2) } if platform.IsWindows && info.Tag == [8]uint8{} { // Windows doesn't support the tag field, clear it for now. pi.Tag = "" } // Start with a clean struct for the second call, otherwise we may get EFAULT. var info2 sys.ProgInfo makeSecondCall := false if info.NrMapIds > 0 { pi.maps = make([]MapID, info.NrMapIds) info2.NrMapIds = info.NrMapIds info2.MapIds = sys.SlicePointer(pi.maps) makeSecondCall = true } else if haveProgramInfoMapIDs() == nil { // This program really has no associated maps. pi.maps = make([]MapID, 0) } else { // The kernel doesn't report associated maps. pi.maps = nil } // createdByUID and NrMapIds were introduced in the same kernel version. if pi.maps != nil && platform.IsLinux { pi.createdByUID = info.CreatedByUid pi.haveCreatedByUID = true } if info.XlatedProgLen > 0 { pi.insns = make([]byte, info.XlatedProgLen) var info3 sys.ProgInfo info3.XlatedProgLen = info.XlatedProgLen info3.XlatedProgInsns = sys.SlicePointer(pi.insns) // When kernel.kptr_restrict and net.core.bpf_jit_harden are both set, it causes the // syscall to abort when trying to readback xlated instructions, skipping other info // as well. So request xlated instructions separately. if err := sys.ObjInfo(fd, &info3); err != nil { return nil, err } if info3.XlatedProgInsns.IsNil() { pi.restricted = true pi.insns = nil } } if info.NrLineInfo > 0 { pi.lineInfos = make([]byte, btf.LineInfoSize*info.NrLineInfo) info2.LineInfo = sys.SlicePointer(pi.lineInfos) info2.LineInfoRecSize = btf.LineInfoSize info2.NrLineInfo = info.NrLineInfo pi.numLineInfos = info.NrLineInfo makeSecondCall = true } if info.NrFuncInfo > 0 { pi.funcInfos = make([]byte, btf.FuncInfoSize*info.NrFuncInfo) info2.FuncInfo = sys.SlicePointer(pi.funcInfos) info2.FuncInfoRecSize = btf.FuncInfoSize info2.NrFuncInfo = info.NrFuncInfo pi.numFuncInfos = info.NrFuncInfo makeSecondCall = true } pi.jitedInfo.lineInfoRecSize = info.JitedLineInfoRecSize if info.JitedProgLen > 0 { pi.jitedInfo.numInsns = info.JitedProgLen pi.jitedInfo.insns = make([]byte, info.JitedProgLen) info2.JitedProgLen = info.JitedProgLen info2.JitedProgInsns = sys.SlicePointer(pi.jitedInfo.insns) makeSecondCall = true } if info.NrJitedFuncLens > 0 { pi.jitedInfo.numFuncLens = info.NrJitedFuncLens pi.jitedInfo.funcLens = make([]uint32, info.NrJitedFuncLens) info2.NrJitedFuncLens = info.NrJitedFuncLens info2.JitedFuncLens = sys.SlicePointer(pi.jitedInfo.funcLens) makeSecondCall = true } if info.NrJitedLineInfo > 0 { pi.jitedInfo.numLineInfos = info.NrJitedLineInfo pi.jitedInfo.lineInfos = make([]uint64, info.NrJitedLineInfo) info2.NrJitedLineInfo = info.NrJitedLineInfo info2.JitedLineInfo = sys.SlicePointer(pi.jitedInfo.lineInfos) info2.JitedLineInfoRecSize = info.JitedLineInfoRecSize makeSecondCall = true } if info.NrJitedKsyms > 0 { pi.jitedInfo.numKsyms = info.NrJitedKsyms pi.jitedInfo.ksyms = make([]uint64, info.NrJitedKsyms) info2.JitedKsyms = sys.SlicePointer(pi.jitedInfo.ksyms) info2.NrJitedKsyms = info.NrJitedKsyms makeSecondCall = true } if makeSecondCall { if err := sys.ObjInfo(fd, &info2); err != nil { return nil, err } if info.JitedProgLen > 0 && info2.JitedProgInsns.IsNil() { // JIT information is not available due to kernel.kptr_restrict pi.jitedInfo.lineInfos = nil pi.jitedInfo.ksyms = nil pi.jitedInfo.insns = nil pi.jitedInfo.funcLens = nil } } if len(pi.Name) == len(info.Name)-1 { // Possibly truncated, check BTF info for full name name, err := readNameFromFunc(&pi) if err == nil { pi.Name = name } // If an error occurs, keep the truncated name, which is better than none } return &pi, nil } func readNameFromFunc(pi *ProgramInfo) (string, error) { if pi.numFuncInfos == 0 { return "", errors.New("no function info") } spec, err := pi.btfSpec() if err != nil { return "", err } funcInfos, err := btf.LoadFuncInfos( bytes.NewReader(pi.funcInfos), internal.NativeEndian, pi.numFuncInfos, spec, ) if err != nil { return "", err } for _, funcInfo := range funcInfos { if funcInfo.Offset == 0 { // Information about the whole program return funcInfo.Func.Name, nil } } return "", errors.New("no function info about program") } func readProgramInfoFromProc(fd *sys.FD, pi *ProgramInfo) error { var progType uint32 err := scanFdInfo(fd, map[string]interface{}{ "prog_type": &progType, "prog_tag": &pi.Tag, "memlock": &pi.memlock, }) if errors.Is(err, ErrNotSupported) && !errors.Is(err, internal.ErrNotSupportedOnOS) { return &internal.UnsupportedFeatureError{ Name: "reading program info from /proc/self/fdinfo", MinimumVersion: internal.Version{4, 10, 0}, } } if err != nil { return err } pi.Type, err = ProgramTypeForPlatform(platform.Linux, progType) if err != nil { return fmt.Errorf("program type: %w", err) } return nil } // ID returns the program ID. // // Available from 4.13. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) ID() (ProgramID, bool) { return pi.id, pi.id > 0 } // CreatedByUID returns the Uid that created the program. // // Available from 4.15. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) CreatedByUID() (uint32, bool) { return pi.createdByUID, pi.haveCreatedByUID } // BTFID returns the BTF ID associated with the program. // // The ID is only valid as long as the associated program is kept alive. // Available from 5.0. // // The bool return value indicates whether this optional field is available and // populated. (The field may be available but not populated if the kernel // supports the field but the program was loaded without BTF information.) func (pi *ProgramInfo) BTFID() (btf.ID, bool) { return pi.btf, pi.btf > 0 } // btfSpec returns the BTF spec associated with the program. func (pi *ProgramInfo) btfSpec() (*btf.Spec, error) { id, ok := pi.BTFID() if !ok { return nil, fmt.Errorf("program created without BTF or unsupported kernel: %w", ErrNotSupported) } h, err := btf.NewHandleFromID(id) if err != nil { return nil, fmt.Errorf("get BTF handle: %w", err) } defer h.Close() spec, err := h.Spec(nil) if err != nil { return nil, fmt.Errorf("get BTF spec: %w", err) } return spec, nil } // ErrRestrictedKernel is returned when kernel address information is restricted // by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls. var ErrRestrictedKernel = internal.ErrRestrictedKernel // LineInfos returns the BTF line information of the program. // // Available from 5.0. // // Returns an error wrapping [ErrRestrictedKernel] if line infos are restricted // by sysctls. // // Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns // ErrNotSupported if the program was created without BTF or if the kernel // doesn't support the field. func (pi *ProgramInfo) LineInfos() (btf.LineOffsets, error) { if len(pi.lineInfos) == 0 { return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) } spec, err := pi.btfSpec() if err != nil { return nil, err } return btf.LoadLineInfos( bytes.NewReader(pi.lineInfos), internal.NativeEndian, pi.numLineInfos, spec, ) } // Instructions returns the 'xlated' instruction stream of the program // after it has been verified and rewritten by the kernel. These instructions // cannot be loaded back into the kernel as-is, this is mainly used for // inspecting loaded programs for troubleshooting, dumping, etc. // // For example, map accesses are made to reference their kernel map IDs, // not the FDs they had when the program was inserted. Note that before // the introduction of bpf_insn_prepare_dump in kernel 4.16, xlated // instructions were not sanitized, making the output even less reusable // and less likely to round-trip or evaluate to the same program Tag. // // The first instruction is marked as a symbol using the Program's name. // // If available, the instructions will be annotated with metadata from the // BTF. This includes line information and function information. Reading // this metadata requires CAP_SYS_ADMIN or equivalent. If capability is // unavailable, the instructions will be returned without metadata. // // Returns an error wrapping [ErrRestrictedKernel] if instructions are // restricted by sysctls. // // Available from 4.13. Requires CAP_BPF or equivalent for plain instructions. // Requires CAP_SYS_ADMIN for instructions with metadata. func (pi *ProgramInfo) Instructions() (asm.Instructions, error) { if platform.IsWindows && len(pi.insns) == 0 { return nil, fmt.Errorf("read instructions: %w", internal.ErrNotSupportedOnOS) } if pi.restricted { return nil, fmt.Errorf("instructions: %w", ErrRestrictedKernel) } // If the calling process is not BPF-capable or if the kernel doesn't // support getting xlated instructions, the field will be zero. if len(pi.insns) == 0 { return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) } r := bytes.NewReader(pi.insns) insns, err := asm.AppendInstructions(nil, r, internal.NativeEndian, platform.Native) if err != nil { return nil, fmt.Errorf("unmarshaling instructions: %w", err) } if pi.btf != 0 { btfh, err := btf.NewHandleFromID(pi.btf) if err != nil { // Getting a BTF handle requires CAP_SYS_ADMIN, if not available we get an -EPERM. // Ignore it and fall back to instructions without metadata. if !errors.Is(err, unix.EPERM) { return nil, fmt.Errorf("unable to get BTF handle: %w", err) } } // If we have a BTF handle, we can use it to assign metadata to the instructions. if btfh != nil { defer btfh.Close() spec, err := btfh.Spec(nil) if err != nil { return nil, fmt.Errorf("unable to get BTF spec: %w", err) } lineInfos, err := btf.LoadLineInfos(bytes.NewReader(pi.lineInfos), internal.NativeEndian, pi.numLineInfos, spec) if err != nil { return nil, fmt.Errorf("parse line info: %w", err) } funcInfos, err := btf.LoadFuncInfos(bytes.NewReader(pi.funcInfos), internal.NativeEndian, pi.numFuncInfos, spec) if err != nil { return nil, fmt.Errorf("parse func info: %w", err) } iter := insns.Iterate() for iter.Next() { assignMetadata(iter.Ins, iter.Offset, &funcInfos, &lineInfos, nil) } } } fn := btf.FuncMetadata(&insns[0]) name := pi.Name if fn != nil { name = fn.Name } insns[0] = insns[0].WithSymbol(name) return insns, nil } // JitedSize returns the size of the program's JIT-compiled machine code in // bytes, which is the actual code executed on the host's CPU. This field // requires the BPF JIT compiler to be enabled. // // Returns an error wrapping [ErrRestrictedKernel] if jited program size is // restricted by sysctls. // // Available from 4.13. Reading this metadata requires CAP_BPF or equivalent. func (pi *ProgramInfo) JitedSize() (uint32, error) { if pi.jitedSize == 0 { return 0, fmt.Errorf("insufficient permissions, unsupported kernel, or JIT compiler disabled: %w", ErrNotSupported) } return pi.jitedSize, nil } // TranslatedSize returns the size of the program's translated instructions in // bytes, after it has been verified and rewritten by the kernel. // // Available from 4.13. Reading this metadata requires CAP_BPF or equivalent. func (pi *ProgramInfo) TranslatedSize() (int, error) { if pi.numInsns == 0 { return 0, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) } return int(pi.numInsns), nil } // MapIDs returns the maps related to the program. // // Available from 4.15. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) MapIDs() ([]MapID, bool) { return pi.maps, pi.maps != nil } // LoadTime returns when the program was loaded since boot time. // // Available from 4.15. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) LoadTime() (time.Duration, bool) { // loadTime and NrMapIds were introduced in the same kernel version. return pi.loadTime, pi.loadTime > 0 } // VerifiedInstructions returns the number verified instructions in the program. // // Available from 5.16. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) { return pi.verifiedInstructions, pi.verifiedInstructions > 0 } // JitedKsymAddrs returns the ksym addresses of the BPF program, including its // subprograms. The addresses correspond to their symbols in /proc/kallsyms. // // Available from 4.18. Note that before 5.x, this field can be empty for // programs without subprograms (bpf2bpf calls). // // The bool return value indicates whether this optional field is available. // // When a kernel address can't fit into uintptr (which is usually the case when // running 32 bit program on a 64 bit kernel), this returns an empty slice and // a false. func (pi *ProgramInfo) JitedKsymAddrs() ([]uintptr, bool) { ksyms := make([]uintptr, 0, len(pi.jitedInfo.ksyms)) if cap(ksyms) == 0 { return ksyms, false } // Check if a kernel address fits into uintptr (it might not when // using a 32 bit binary on a 64 bit kernel). This check should work // with any kernel address, since they have 1s at the highest bits. if a := pi.jitedInfo.ksyms[0]; uint64(uintptr(a)) != a { return nil, false } for _, ksym := range pi.jitedInfo.ksyms { ksyms = append(ksyms, uintptr(ksym)) } return ksyms, true } // JitedInsns returns the JITed machine native instructions of the program. // // Available from 4.13. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) JitedInsns() ([]byte, bool) { return pi.jitedInfo.insns, len(pi.jitedInfo.insns) > 0 } // JitedLineInfos returns the JITed line infos of the program. // // Available from 5.0. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) JitedLineInfos() ([]uint64, bool) { return pi.jitedInfo.lineInfos, len(pi.jitedInfo.lineInfos) > 0 } // JitedFuncLens returns the insns length of each function in the JITed program. // // Available from 4.18. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) JitedFuncLens() ([]uint32, bool) { return pi.jitedInfo.funcLens, len(pi.jitedInfo.funcLens) > 0 } // FuncInfos returns the offset and function information of all (sub)programs in // a BPF program. // // Available from 5.0. // // Returns an error wrapping [ErrRestrictedKernel] if function information is // restricted by sysctls. // // Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns // ErrNotSupported if the program was created without BTF or if the kernel // doesn't support the field. func (pi *ProgramInfo) FuncInfos() (btf.FuncOffsets, error) { if len(pi.funcInfos) == 0 { return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) } spec, err := pi.btfSpec() if err != nil { return nil, err } return btf.LoadFuncInfos( bytes.NewReader(pi.funcInfos), internal.NativeEndian, pi.numFuncInfos, spec, ) } // ProgramInfo returns an approximate number of bytes allocated to this program. // // Available from 4.10. // // The bool return value indicates whether this optional field is available. func (pi *ProgramInfo) Memlock() (uint64, bool) { return pi.memlock, pi.memlock > 0 } func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { if platform.IsWindows { return fmt.Errorf("read fdinfo: %w", internal.ErrNotSupportedOnOS) } fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int())) if err != nil { return err } defer fh.Close() if err := scanFdInfoReader(fh, fields); err != nil { return fmt.Errorf("%s: %w", fh.Name(), err) } return nil } func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { var ( scanner = bufio.NewScanner(r) scanned int reader bytes.Reader ) for scanner.Scan() { key, rest, found := bytes.Cut(scanner.Bytes(), []byte(":")) if !found { // Line doesn't contain a colon, skip. continue } field, ok := fields[string(key)] if !ok { continue } // If field already contains a non-zero value, don't overwrite it with fdinfo. if !zero(field) { scanned++ continue } // Cut the \t following the : as well as any potential trailing whitespace. rest = bytes.TrimSpace(rest) reader.Reset(rest) if n, err := fmt.Fscan(&reader, field); err != nil || n != 1 { return fmt.Errorf("can't parse field %s: %v", key, err) } scanned++ } if err := scanner.Err(); err != nil { return fmt.Errorf("scanning fdinfo: %w", err) } if len(fields) > 0 && scanned == 0 { return ErrNotSupported } return nil } func zero(arg any) bool { v := reflect.ValueOf(arg) // Unwrap pointers and interfaces. for v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface { v = v.Elem() } return v.IsZero() } // EnableStats starts collecting runtime statistics of eBPF programs, like the // amount of program executions and the cumulative runtime. // // Specify a BPF_STATS_* constant to select which statistics to collect, like // [unix.BPF_STATS_RUN_TIME]. Closing the returned [io.Closer] will stop // collecting statistics. // // Collecting statistics may have a performance impact. // // Requires at least Linux 5.8. func EnableStats(which uint32) (io.Closer, error) { fd, err := sys.EnableStats(&sys.EnableStatsAttr{ Type: which, }) if err != nil { return nil, err } return fd, nil } var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", func() error { if platform.IsWindows { // We only support efW versions which have this feature, no need to probe. return nil } prog, err := progLoad(asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, SocketFilter, "MIT") if err != nil { return err } defer prog.Close() err = sys.ObjInfo(prog, &sys.ProgInfo{ // NB: Don't need to allocate MapIds since the program isn't using // any maps. NrMapIds: 1, }) if errors.Is(err, unix.EINVAL) { // Most likely the syscall doesn't exist. return internal.ErrNotSupported } if errors.Is(err, unix.E2BIG) { // We've hit check_uarg_tail_zero on older kernels. return internal.ErrNotSupported } return err }, "4.15", "windows:0.21.0") golang-github-cilium-ebpf-0.21.0+ds1/info_test.go000066400000000000000000000353301520243672000215240ustar00rootroot00000000000000package ebpf import ( "fmt" "os" "reflect" "runtime" "strings" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" ) var btfFn = &btf.Func{ Name: "_", Type: &btf.FuncProto{ Return: &btf.Int{Size: 16}, Params: []btf.FuncParam{}, }, Linkage: btf.StaticFunc, } var hashMapSpec = &MapSpec{ Type: Hash, KeySize: 4, ValueSize: 5, MaxEntries: 2, Flags: sys.BPF_F_NO_PREALLOC, } var multiprogSpec = &ProgramSpec{ Name: "test", Type: SocketFilter, Instructions: asm.Instructions{ btf.WithFuncMetadata(asm.LoadImm(asm.R0, 0, asm.DWord), btfFn). WithSource(asm.Comment("line info")), asm.Call.Label("fn"), asm.Return(), btf.WithFuncMetadata(asm.LoadImm(asm.R0, 0, asm.DWord), btfFn). WithSource(asm.Comment("line info")).WithSymbol("fn"), asm.Return(), }, License: "MIT", } func validateMapInfo(t *testing.T, info *MapInfo, spec *MapSpec) { t.Helper() qt.Assert(t, qt.Equals(info.Type, spec.Type)) qt.Assert(t, qt.Equals(info.KeySize, spec.KeySize)) qt.Assert(t, qt.Equals(info.ValueSize, spec.ValueSize)) qt.Assert(t, qt.Equals(info.MaxEntries, spec.MaxEntries)) qt.Assert(t, qt.Equals(info.Flags, spec.Flags)) memlock, _ := info.Memlock() qt.Assert(t, qt.Not(qt.Equals(memlock, 0))) } func TestMapInfo(t *testing.T) { m := mustNewMap(t, hashMapSpec, nil) info, err := m.Info() qt.Assert(t, qt.IsNil(err)) validateMapInfo(t, info, hashMapSpec) } func TestMapInfoFromProc(t *testing.T) { hash := mustNewMap(t, hashMapSpec, nil) var info MapInfo err := readMapInfoFromProc(hash.fd, &info) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) validateMapInfo(t, &info, hashMapSpec) } func TestMapInfoFromProcOuterMap(t *testing.T) { outer := &MapSpec{ Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, } m := mustNewMap(t, outer, nil) var info MapInfo err := readMapInfoFromProc(m.fd, &info) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) validateMapInfo(t, &info, outer) } func BenchmarkNewMapFromFD(b *testing.B) { b.ReportAllocs() m := mustNewMap(b, hashMapSpec, nil) for b.Loop() { if _, err := newMapFromFD(m.fd); err != nil { b.Fatal(err) } } } func BenchmarkMapInfo(b *testing.B) { b.ReportAllocs() m := mustNewMap(b, hashMapSpec, nil) for b.Loop() { if _, err := newMapInfoFromFd(m.fd); err != nil { b.Fatal(err) } } } func validateProgInfo(t *testing.T, spec *ProgramSpec, info *ProgramInfo) { t.Helper() qt.Assert(t, qt.Equals(info.Type, spec.Type)) if info.Tag != "" { qt.Assert(t, qt.SliceAny( []string{ "d7edec644f05498d", // SHA1, pre-6.18 "01e57aadad14352b", // SHA256 }, qt.F2(qt.Equals, info.Tag), )) } memlock, ok := info.Memlock() if ok { qt.Assert(t, qt.Equals(memlock, 4096)) } } func TestProgramInfo(t *testing.T) { spec := fixupProgramSpec(basicProgramSpec) prog := mustNewProgram(t, spec, nil) info, err := newProgramInfoFromFd(prog.fd) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) validateProgInfo(t, spec, info) id, ok := info.ID() qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Not(qt.Equals(id, 0))) if testutils.IsVersionLessThan(t, "4.15", "windows:0.20") { qt.Assert(t, qt.Equals(info.Name, "")) } else { qt.Assert(t, qt.Equals(info.Name, "test")) } if jitedSize, err := info.JitedSize(); testutils.IsVersionLessThan(t, "4.13") { qt.Assert(t, qt.IsNotNil(err)) } else { qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(jitedSize > 0)) } if xlatedSize, err := info.TranslatedSize(); testutils.IsVersionLessThan(t, "4.13") { qt.Assert(t, qt.IsNotNil(err)) } else { qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(xlatedSize > 0)) } if uid, ok := info.CreatedByUID(); testutils.IsVersionLessThan(t, "4.15") { qt.Assert(t, qt.IsFalse(ok)) } else { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(uid, uint32(os.Getuid()))) } if loadTime, ok := info.LoadTime(); testutils.IsVersionLessThan(t, "4.15") { qt.Assert(t, qt.IsFalse(ok)) } else { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsTrue(loadTime > 0)) } if verifiedInsns, ok := info.VerifiedInstructions(); testutils.IsVersionLessThan(t, "5.16") { qt.Assert(t, qt.IsFalse(ok)) } else { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsTrue(verifiedInsns > 0)) } if insns, ok := info.JitedInsns(); testutils.IsVersionLessThan(t, "4.13") { qt.Assert(t, qt.IsFalse(ok)) } else { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsTrue(len(insns) > 0)) } } func BenchmarkNewProgramFromFD(b *testing.B) { b.ReportAllocs() spec := fixupProgramSpec(basicProgramSpec) prog := mustNewProgram(b, spec, nil) for b.Loop() { if _, err := newProgramFromFD(prog.fd); err != nil { b.Fatal(err) } } } func BenchmarkProgramInfo(b *testing.B) { b.ReportAllocs() spec := fixupProgramSpec(basicProgramSpec) prog := mustNewProgram(b, spec, nil) for b.Loop() { if _, err := newProgramInfoFromFd(prog.fd); err != nil { b.Fatal(err) } } } func TestProgramInfoProc(t *testing.T) { spec := fixupProgramSpec(basicProgramSpec) prog := mustNewProgram(t, spec, nil) var info ProgramInfo err := readProgramInfoFromProc(prog.fd, &info) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) validateProgInfo(t, spec, &info) } func TestProgramInfoBTF(t *testing.T) { prog, err := newProgram(t, multiprogSpec, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) info, err := prog.Info() testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) // On kernels before 5.x, nr_jited_ksyms is not set for programs without subprogs. // It's included here since this test uses a bpf program with subprogs. if addrs, ok := info.JitedKsymAddrs(); testutils.IsVersionLessThan(t, "4.18") { qt.Assert(t, qt.IsFalse(ok)) } else { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsTrue(len(addrs) > 0)) } if lens, ok := info.JitedFuncLens(); testutils.IsVersionLessThan(t, "4.18") { qt.Assert(t, qt.IsFalse(ok)) } else { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsTrue(len(lens) > 0)) } if infos, ok := info.JitedLineInfos(); testutils.IsVersionLessThan(t, "5.0") { qt.Assert(t, qt.IsFalse(ok)) } else { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsTrue(len(infos) > 0)) } if funcs, err := info.FuncInfos(); testutils.IsVersionLessThan(t, "5.0") { qt.Assert(t, qt.IsNotNil(err)) } else { qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.HasLen(funcs, 2)) qt.Assert(t, qt.ContentEquals(funcs[0].Func, btfFn)) qt.Assert(t, qt.ContentEquals(funcs[1].Func, btfFn)) } if lines, err := info.LineInfos(); testutils.IsVersionLessThan(t, "5.0") { qt.Assert(t, qt.IsNotNil(err)) } else { qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.HasLen(lines, 2)) qt.Assert(t, qt.Equals(lines[0].Line.Line(), "line info")) qt.Assert(t, qt.Equals(lines[1].Line.Line(), "line info")) } } func TestProgramInfoMapIDs(t *testing.T) { arr := createMap(t, Array, 1) prog := mustNewProgram(t, &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadMapPtr(asm.R0, arr.FD()), asm.LoadImm(asm.R0, 2, asm.DWord), asm.Return(), }, License: "MIT", }, nil) info, err := prog.Info() testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) ids, ok := info.MapIDs() switch { case testutils.IsVersionLessThan(t, "4.15", "windows:0.20"): qt.Assert(t, qt.IsFalse(ok)) qt.Assert(t, qt.HasLen(ids, 0)) default: qt.Assert(t, qt.IsTrue(ok)) mapInfo, err := arr.Info() qt.Assert(t, qt.IsNil(err)) mapID, ok := mapInfo.ID() qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.ContentEquals(ids, []MapID{mapID})) } } func TestProgramInfoMapIDsNoMaps(t *testing.T) { prog := createBasicProgram(t) info, err := prog.Info() testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) ids, ok := info.MapIDs() switch { case testutils.IsVersionLessThan(t, "4.15", "windows:0.20"): qt.Assert(t, qt.IsFalse(ok)) qt.Assert(t, qt.HasLen(ids, 0)) default: qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.HasLen(ids, 0)) } } func TestScanFdInfoReader(t *testing.T) { tests := []struct { fields map[string]interface{} valid bool }{ {nil, true}, {map[string]interface{}{"foo": new(string)}, true}, {map[string]interface{}{"zap": new(string)}, false}, {map[string]interface{}{"foo": new(int)}, false}, } for _, test := range tests { err := scanFdInfoReader(strings.NewReader("foo:\tbar\n"), test.fields) if test.valid { if err != nil { t.Errorf("fields %v returns an error: %s", test.fields, err) } } else { if err == nil { t.Errorf("fields %v doesn't return an error", test.fields) } } } } func BenchmarkScanFdInfoReader(b *testing.B) { b.ReportAllocs() // Pathological case with 9 fields we're not interested in, and one // field we are, all the way at the very end. input := strings.Repeat("ignore:\tthis\n", 9) input += "foo:\tbar\n" r := strings.NewReader(input) var val string fields := map[string]any{"foo": &val} for b.Loop() { val = "" r.Reset(input) if err := scanFdInfoReader(r, fields); err != nil { b.Fatal(err) } if val != "bar" { b.Fatal("unexpected value:", val) } } } // TestProgramStats loads a BPF program once and executes back-to-back test runs // of the program. See testStats for details. func TestProgramStats(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF_ENABLE_STATS") prog := createBasicProgram(t) s, err := prog.Stats() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(s.RunCount, 0)) qt.Assert(t, qt.Equals(s.RecursionMisses, 0)) if runtime.GOARCH != "arm64" { // Runtime is flaky on arm64. qt.Assert(t, qt.Equals(s.Runtime, 0)) } if err := testStats(t, prog); err != nil { testutils.SkipIfNotSupportedOnOS(t, err) t.Error(err) } } // BenchmarkStats is a benchmark of TestStats. See testStats for details. func BenchmarkStats(b *testing.B) { b.ReportAllocs() testutils.SkipOnOldKernel(b, "5.8", "BPF_ENABLE_STATS") prog := createBasicProgram(b) for b.Loop() { if err := testStats(b, prog); err != nil { testutils.SkipIfNotSupportedOnOS(b, err) b.Fatal(err) } } } // testStats implements the behaviour under test for TestStats // and BenchmarkStats. First, a test run is executed with runtime statistics // enabled, followed by another with runtime stats disabled. Counters are only // expected to increase on the runs where runtime stats are enabled. // // Due to runtime behaviour on Go 1.14 and higher, the syscall backing // (*Program).Test() could be invoked multiple times for each call to Test(), // resulting in RunCount incrementing by more than one. Expecting RunCount to // be of a specific value after a call to Test() is therefore not possible. // See https://golang.org/doc/go1.14#runtime for more details. func testStats(tb testing.TB, prog *Program) error { tb.Helper() in := internal.EmptyBPFContext stats, err := EnableStats(uint32(sys.BPF_STATS_RUN_TIME)) if err != nil { return fmt.Errorf("failed to enable stats: %w", err) } defer stats.Close() // Program execution with runtime statistics enabled. // Should increase both runtime and run counter. mustRun(tb, prog, &RunOptions{Data: in}) s1, err := prog.Stats() qt.Assert(tb, qt.IsNil(err)) qt.Assert(tb, qt.Not(qt.Equals(s1.RunCount, 0)), qt.Commentf("expected run count to be at least 1 after first invocation")) qt.Assert(tb, qt.Not(qt.Equals(s1.Runtime, 0)), qt.Commentf("expected runtime to be at least 1ns after first invocation")) qt.Assert(tb, qt.IsNil(stats.Close())) // Second program execution, with runtime statistics gathering disabled. // Total runtime and run counters are not expected to increase. mustRun(tb, prog, &RunOptions{Data: in}) s2, err := prog.Stats() qt.Assert(tb, qt.IsNil(err)) qt.Assert(tb, qt.Equals(s2.RunCount, s1.RunCount), qt.Commentf("run count (%d) increased after first invocation (%d)", s2.RunCount, s1.RunCount)) qt.Assert(tb, qt.Equals(s2.Runtime, s1.Runtime), qt.Commentf("runtime (%d) increased after first invocation (%d)", s2.Runtime, s1.Runtime)) return nil } func TestHaveProgramInfoMapIDs(t *testing.T) { testutils.CheckFeatureTest(t, haveProgramInfoMapIDs) } func TestProgInfoExtBTF(t *testing.T) { testutils.SkipOnOldKernel(t, "5.0", "Program BTF (func/line_info)") spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/loader-%s.elf")) if err != nil { t.Fatal(err) } var obj struct { Main *Program `ebpf:"xdp_prog"` } err = loadAndAssign(t, spec, &obj, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer obj.Main.Close() info, err := obj.Main.Info() if err != nil { t.Fatal(err) } inst, err := info.Instructions() if err != nil { t.Fatal(err) } expectedLineInfoCount := 28 expectedFuncInfo := map[string]bool{ "xdp_prog": false, "static_fn": false, "global_fn2": false, "global_fn3": false, } lineInfoCount := 0 for _, ins := range inst { if ins.Source() != nil { lineInfoCount++ } fn := btf.FuncMetadata(&ins) if fn != nil { expectedFuncInfo[fn.Name] = true } } if lineInfoCount != expectedLineInfoCount { t.Errorf("expected %d line info entries, got %d", expectedLineInfoCount, lineInfoCount) } for fn, found := range expectedFuncInfo { if !found { t.Errorf("func %q not found", fn) } } } func TestInfoExportedFields(t *testing.T) { // It is highly unlikely that you should be adjusting the asserts below. // See the comment at the top of info.go for more information. var names []string for _, field := range reflect.VisibleFields(reflect.TypeOf(MapInfo{})) { if field.IsExported() { names = append(names, field.Name) } } qt.Assert(t, qt.ContentEquals(names, []string{ "Type", "KeySize", "ValueSize", "MaxEntries", "Flags", "Name", })) names = nil for _, field := range reflect.VisibleFields(reflect.TypeOf(ProgramInfo{})) { if field.IsExported() { names = append(names, field.Name) } } qt.Assert(t, qt.ContentEquals(names, []string{ "Type", "Tag", "Name", })) } func TestZero(t *testing.T) { var ( empty = "" nul uint32 = 0 one uint32 = 1 iempty any = "" inul any = uint32(0) ione any = uint32(1) ) qt.Assert(t, qt.IsTrue(zero(empty))) qt.Assert(t, qt.IsTrue(zero(nul))) qt.Assert(t, qt.IsFalse(zero(one))) qt.Assert(t, qt.IsTrue(zero(&empty))) qt.Assert(t, qt.IsTrue(zero(&nul))) qt.Assert(t, qt.IsFalse(zero(&one))) qt.Assert(t, qt.IsTrue(zero(iempty))) qt.Assert(t, qt.IsTrue(zero(inul))) qt.Assert(t, qt.IsFalse(zero(ione))) qt.Assert(t, qt.IsTrue(zero(&iempty))) qt.Assert(t, qt.IsTrue(zero(&inul))) qt.Assert(t, qt.IsFalse(zero(&ione))) } golang-github-cilium-ebpf-0.21.0+ds1/internal/000077500000000000000000000000001520243672000210135ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/cmd/000077500000000000000000000000001520243672000215565ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/cmd/genfunctions.awk000066400000000000000000000016371520243672000247730ustar00rootroot00000000000000#!/usr/bin/gawk -f # Generate constants from Linux headers. # # This script expects include/uapi/bpf.h as input. BEGIN { print "// Code generated by internal/cmd/genfunctions.awk; DO NOT EDIT." print "" print "package asm" print "" print "// Code in this file is derived from Linux, available under the GPL-2.0 WITH Linux-syscall-note." print "" print "import \"github.com/cilium/ebpf/internal/platform\"" print "" print "// Built-in functions (Linux)." print "const (" } /FN\([[:alnum:]_]+, [[:digit:]]+,.*\)/ { name = gensub(/.*FN\(([[:alnum:]_]+), [[:digit:]]+,.*\).*/, "\\1", 1) id = gensub(/.*FN\([[:alnum:]_]+, ([[:digit:]]+),.*\).*/, "\\1", 1) split(tolower(name), parts, "_") result = "Fn" for (i in parts) { part = parts[i] result = result substr(toupper(substr(part,1,1)), 1, 1) substr(part, 2) } print "\t" result " = BuiltinFunc(platform.LinuxTag | " id ")" } END { print ")" print "" } golang-github-cilium-ebpf-0.21.0+ds1/internal/cmd/gensections.awk000077500000000000000000000015771520243672000246200ustar00rootroot00000000000000#!/usr/bin/gawk -f # This script expects tools/lib/bpf/libbpf.c as input. function trim(str, left, right) { str = gensub("^[\t ]*" left, "", "g", str) return gensub(right "$", "", "g", str) } BEGIN { print "// Code generated by internal/cmd/gensections.awk; DO NOT EDIT." print "" print "package ebpf" print "" print "// Code in this file is derived from libbpf, available under BSD-2-Clause." print "" print "import \"github.com/cilium/ebpf/internal/sys\"" print "" print "var elfSectionDefs = []libbpfElfSectionDef{" FS="," } /\tSEC_DEF/ { pattern = trim(substr($1, 10)) prog_type = "sys.BPF_PROG_TYPE_" trim($2) attach_type = trim($3) attach_type = attach_type == "0" ? "0" : "sys." attach_type flags = trim($4, "", ")") flags = gensub("SEC_", "_SEC_", "g", flags) printf "\t{%s, %s, %s, %s},\n", pattern, prog_type, attach_type, flags; } END { print "}" print "" } golang-github-cilium-ebpf-0.21.0+ds1/internal/cmd/gentypes/000077500000000000000000000000001520243672000234145ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/cmd/gentypes/.gitignore000066400000000000000000000000111520243672000253740ustar00rootroot00000000000000gentypes golang-github-cilium-ebpf-0.21.0+ds1/internal/cmd/gentypes/main.go000066400000000000000000000677761520243672000247160ustar00rootroot00000000000000// Program gentypes reads a compressed vmlinux .BTF section and generates // syscall bindings from it. // // Output is written to "types.go". package main import ( "bytes" "errors" "flag" "fmt" "os" "slices" "sort" "strconv" "strings" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) type syscallRetval int const ( retError syscallRetval = iota retFd ) var ( verbose bool ) func debugln(a ...any) { if verbose { fmt.Println(a...) } } func getBool(key string, defaultVal bool) bool { val, ok := os.LookupEnv(key) if !ok { return defaultVal } b, err := strconv.ParseBool(val) if err != nil { return defaultVal } return b } func main() { fs := flag.NewFlagSet("gentypes", flag.ContinueOnError) fs.BoolVar(&verbose, "verbose", getBool("V", false), "Enable verbose logging ($V)") if err := fs.Parse(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } if err := run(fs.Args()); err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } } func run(args []string) error { if len(args) != 1 { return fmt.Errorf("expect location of compressed vmlinux .BTF as argument") } raw, err := internal.ReadAllCompressed(args[0]) if err != nil { return err } spec, err := btf.LoadSpecFromReader(bytes.NewReader(raw)) if err != nil { return err } output, err := generateTypes(spec) var fpe *failedPatchError if errors.As(err, &fpe) { fmt.Fprintf(os.Stderr, " %v\n", fpe.Type) for _, member := range fpe.Type.Members { fmt.Fprintf(os.Stderr, " %q %v\n", member.Name, member.Type) } } if err != nil { return err } w, err := os.Create("types.go") if err != nil { return err } defer w.Close() return internal.WriteFormatted(output, w) } func generateTypes(spec *btf.Spec) ([]byte, error) { objName := &btf.Array{Nelems: 16, Type: &btf.Int{Encoding: btf.Char, Size: 1}} mapID := &btf.Int{Size: 4} progID := &btf.Int{Size: 4} linkID := &btf.Int{Size: 4} btfID := &btf.Int{Size: 4} typeID := &btf.Int{Size: 4} logLevel := &btf.Int{Size: 4} rawPointer := &btf.Pointer{} stringPointer := &btf.Pointer{} stringSlicePointer := &btf.Pointer{} bytePtr := &btf.Pointer{} int32Ptr := &btf.Pointer{} uint32Ptr := &btf.Pointer{} uint64Ptr := &btf.Pointer{} uintptrPointer := &btf.Pointer{} mapIDPtr := &btf.Pointer{} progIDPtr := &btf.Pointer{} linkIDPtr := &btf.Pointer{} gf := &btf.GoFormatter{ Names: map[btf.Type]string{ objName: internal.GoTypeName(sys.ObjName{}), mapID: internal.GoTypeName(sys.MapID(0)), progID: internal.GoTypeName(sys.ProgramID(0)), linkID: internal.GoTypeName(sys.LinkID(0)), btfID: internal.GoTypeName(sys.BTFID(0)), typeID: internal.GoTypeName(sys.TypeID(0)), logLevel: internal.GoTypeName(sys.LogLevel(0)), rawPointer: internal.GoTypeName(sys.Pointer{}), stringPointer: internal.GoTypeName(sys.StringPointer{}), stringSlicePointer: internal.GoTypeName(sys.StringSlicePointer{}), bytePtr: internal.GoTypeName(sys.TypedPointer[byte]{}), int32Ptr: internal.GoTypeName(sys.TypedPointer[int32]{}), uint32Ptr: internal.GoTypeName(sys.TypedPointer[uint32]{}), uint64Ptr: internal.GoTypeName(sys.TypedPointer[uint64]{}), uintptrPointer: internal.GoTypeName(sys.TypedPointer[uintptr]{}), mapIDPtr: internal.GoTypeName(sys.TypedPointer[sys.MapID]{}), progIDPtr: internal.GoTypeName(sys.TypedPointer[sys.ProgramID]{}), linkIDPtr: internal.GoTypeName(sys.TypedPointer[sys.LinkID]{}), }, Identifier: internal.Identifier, EnumIdentifier: func(name, element string) string { return element }, } w := bytes.NewBuffer(nil) w.WriteString(`// Code generated by internal/cmd/gentypes; DO NOT EDIT. package sys import ( "unsafe" "structs" ) `) // Constants (aka unnamed enums) var consts []btf.EnumValue for typ, err := range spec.All() { if err != nil { return nil, err } e, ok := typ.(*btf.Enum) if !ok { continue } if e.Name != "" { continue } for _, value := range e.Values { if strings.HasPrefix(value.Name, "BPF_") { // Greedily take all values which start with BPF_ consts = append(consts, value) } } } sort.Slice(consts, func(i, j int) bool { return consts[i].Name < consts[j].Name }) var nconsts uint32 w.WriteString("const (\n") for _, c := range consts { debugln("const", c.Name) fmt.Fprintf(w, "\t%s = %v\n", c.Name, c.Value) nconsts++ } w.WriteString(")\n") fmt.Printf("Generated %d constants\n", nconsts) // Typed constants (aka named enums) enums := []struct { goType string cType string }{ {"Cmd", "bpf_cmd"}, {"ObjType", "bpf_type"}, {"MapType", "bpf_map_type"}, {"ProgType", "bpf_prog_type"}, {"AttachType", "bpf_attach_type"}, {"LinkType", "bpf_link_type"}, {"StatsType", "bpf_stats_type"}, {"SkAction", "sk_action"}, {"StackBuildIdStatus", "bpf_stack_build_id_status"}, {"FunctionId", "bpf_func_id"}, {"AdjRoomMode", "bpf_adj_room_mode"}, {"HdrStartOff", "bpf_hdr_start_off"}, {"RetCode", "bpf_ret_code"}, {"XdpAction", "xdp_action"}, {"TcxActionBase", "tcx_action_base"}, {"PerfEventType", "bpf_perf_event_type"}, {"NetfilterInetHook", "nf_inet_hooks"}, } sort.Slice(enums, func(i, j int) bool { return enums[i].goType < enums[j].goType }) var nenums uint32 enumTypes := make(map[string]btf.Type) outputEnum := func(goType string, t *btf.Enum) error { // Add the enum as a predeclared type so that generated structs // refer to the Go types. if name := gf.Names[t]; name != "" { return fmt.Errorf("type %q is already declared as %s", goType, name) } gf.Names[t] = goType enumTypes[goType] = t decl, err := gf.TypeDeclaration(goType, t) if err != nil { return fmt.Errorf("generate %q: %w", goType, err) } w.WriteString(decl) w.WriteRune('\n') nenums++ return nil } for _, o := range enums { debugln("enum", o.goType) var t *btf.Enum if err := spec.TypeByName(o.cType, &t); err != nil { return nil, err } if err := outputEnum(o.goType, t); err != nil { return nil, err } } // Unnamed enums // We can't identify them by name, so instead, we specify a prefix that all enum values must share untypedEnums := []struct { goType string prefix string }{ {"NetfilterProtocolFamily", "NFPROTO_"}, } for _, o := range untypedEnums { debugln("enum", o.goType) for anyType, err := range spec.All() { if err != nil { return nil, err } enum, ok := anyType.(*btf.Enum) if !ok { continue } var prefixMatches = true for _, value := range enum.Values { if !strings.HasPrefix(value.Name, o.prefix) { prefixMatches = false break } } if !prefixMatches || len(enum.Values) == 0 { continue } if err := outputEnum(o.goType, enum); err != nil { return nil, err } } } fmt.Printf("Generated %d enums\n", nenums) // Assorted structs structs := []struct { goType string cType string patches []patch }{ { "ProgInfo", "bpf_prog_info", []patch{ replace(objName, "name"), replace(bytePtr, "jited_prog_insns"), replace(bytePtr, "xlated_prog_insns"), replace(mapIDPtr, "map_ids"), replace(bytePtr, "line_info"), replace(uint64Ptr, "jited_line_info"), replace(uint64Ptr, "jited_ksyms"), replace(uint32Ptr, "jited_func_lens"), replace(bytePtr, "func_info"), replace(btfID, "btf_id", "attach_btf_obj_id"), replace(typeID, "attach_btf_id"), }, }, { "MapInfo", "bpf_map_info", []patch{ replace(mapID, "id"), replace(objName, "name"), replace(typeID, "btf_vmlinux_value_type_id", "btf_key_type_id", "btf_value_type_id"), }, }, { "BtfInfo", "bpf_btf_info", []patch{ replace(bytePtr, "btf", "name"), replace(btfID, "id"), }, }, { "LinkInfo", "bpf_link_info", []patch{ replace(enumTypes["LinkType"], "type"), replace(linkID, "id"), name(3, "extra"), replaceWithBytes("extra"), }, }, {"FuncInfo", "bpf_func_info", nil}, {"LineInfo", "bpf_line_info", nil}, {"XdpMd", "xdp_md", nil}, { "SkLookup", "bpf_sk_lookup", []patch{ choose(0, "cookie"), replaceWithBytes("remote_ip4", "remote_ip6", "local_ip4", "local_ip6"), }, }, } sort.Slice(structs, func(i, j int) bool { return structs[i].goType < structs[j].goType }) var nstructs uint32 for _, s := range structs { debugln("struct", s.goType) var t *btf.Struct if err := spec.TypeByName(s.cType, &t); err != nil { return nil, err } if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil { return nil, fmt.Errorf("output %q: %w", s.goType, err) } nstructs++ } fmt.Printf("Generated %d structs\n", nstructs) // Attrs attrs := []struct { goType string ret syscallRetval cType string cmd string patches []patch }{ { "MapCreate", retFd, "map_create", "BPF_MAP_CREATE", []patch{ replace(objName, "map_name"), replace(enumTypes["MapType"], "map_type"), replace(typeID, "btf_vmlinux_value_type_id", "btf_key_type_id", "btf_value_type_id"), }, }, { "MapLookupElem", retError, "map_elem", "BPF_MAP_LOOKUP_ELEM", []patch{choose(2, "value"), replace(rawPointer, "key", "value")}, }, { "MapLookupAndDeleteElem", retError, "map_elem", "BPF_MAP_LOOKUP_AND_DELETE_ELEM", []patch{choose(2, "value"), replace(rawPointer, "key", "value")}, }, { "MapUpdateElem", retError, "map_elem", "BPF_MAP_UPDATE_ELEM", []patch{choose(2, "value"), replace(rawPointer, "key", "value")}, }, { "MapDeleteElem", retError, "map_elem", "BPF_MAP_DELETE_ELEM", []patch{choose(2, "value"), replace(rawPointer, "key", "value")}, }, { "MapGetNextKey", retError, "map_elem", "BPF_MAP_GET_NEXT_KEY", []patch{ choose(2, "next_key"), replace(rawPointer, "key", "next_key"), truncateAfter("next_key"), }, }, { "MapFreeze", retError, "map_elem", "BPF_MAP_FREEZE", []patch{truncateAfter("map_fd")}, }, { "MapLookupBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_BATCH", []patch{replace(rawPointer, "in_batch", "out_batch", "keys", "values")}, }, { "MapLookupAndDeleteBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_AND_DELETE_BATCH", []patch{replace(rawPointer, "in_batch", "out_batch", "keys", "values")}, }, { "MapUpdateBatch", retError, "map_elem_batch", "BPF_MAP_UPDATE_BATCH", []patch{replace(rawPointer, "in_batch", "out_batch", "keys", "values")}, }, { "MapDeleteBatch", retError, "map_elem_batch", "BPF_MAP_DELETE_BATCH", []patch{replace(rawPointer, "in_batch", "out_batch", "keys", "values")}, }, { "ProgLoad", retFd, "prog_load", "BPF_PROG_LOAD", []patch{ replace(objName, "prog_name"), replace(enumTypes["ProgType"], "prog_type"), replace(enumTypes["AttachType"], "expected_attach_type"), replace(logLevel, "log_level"), replace(bytePtr, "insns"), replace(stringPointer, "license"), replace(bytePtr, "log_buf"), replace(bytePtr, "func_info", "line_info", "core_relos", ), replace(int32Ptr, "fd_array"), replace(typeID, "attach_btf_id"), choose(20, "attach_btf_obj_fd"), }, }, { "ProgBindMap", retError, "prog_bind_map", "BPF_PROG_BIND_MAP", nil, }, { "ObjPin", retError, "obj_pin", "BPF_OBJ_PIN", []patch{replace(stringPointer, "pathname")}, }, { "ObjGet", retFd, "obj_pin", "BPF_OBJ_GET", []patch{replace(stringPointer, "pathname")}, }, { "ProgAttach", retError, "prog_attach", "BPF_PROG_ATTACH", []patch{ flattenAnon, rename("target_fd", "target_fd_or_ifindex"), rename("relative_fd", "relative_fd_or_id"), }, }, { "ProgDetach", retError, "prog_attach", "BPF_PROG_DETACH", []patch{ flattenAnon, rename("target_fd", "target_fd_or_ifindex"), truncateAfter("expected_revision"), rename("relative_fd", "relative_fd_or_id"), remove("replace_bpf_fd"), }, }, { "ProgRun", retError, "prog_run", "BPF_PROG_TEST_RUN", []patch{replace(bytePtr, "data_in", "data_out", "ctx_in", "ctx_out")}, }, { "ProgGetNextId", retError, "obj_next_id", "BPF_PROG_GET_NEXT_ID", []patch{ choose(0, "start_id"), rename("start_id", "id"), truncateAfter("next_id"), }, }, { "MapGetNextId", retError, "obj_next_id", "BPF_MAP_GET_NEXT_ID", []patch{ choose(0, "start_id"), rename("start_id", "id"), truncateAfter("next_id"), }, }, { "BtfGetNextId", retError, "obj_next_id", "BPF_BTF_GET_NEXT_ID", []patch{ choose(0, "start_id"), rename("start_id", "id"), replace(btfID, "id", "next_id"), truncateAfter("next_id"), }, }, { "LinkGetNextId", retError, "obj_next_id", "BPF_LINK_GET_NEXT_ID", []patch{ choose(0, "start_id"), rename("start_id", "id"), replace(linkID, "id", "next_id"), truncateAfter("next_id"), }, }, // These piggy back on the obj_next_id decl, but only support the // first field... { "BtfGetFdById", retFd, "obj_next_id", "BPF_BTF_GET_FD_BY_ID", []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, }, { "MapGetFdById", retFd, "obj_next_id", "BPF_MAP_GET_FD_BY_ID", []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, }, { "ProgGetFdById", retFd, "obj_next_id", "BPF_PROG_GET_FD_BY_ID", []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, }, { "LinkGetFdById", retFd, "obj_next_id", "BPF_LINK_GET_FD_BY_ID", []patch{choose(0, "start_id"), rename("start_id", "id"), replace(linkID, "id"), truncateAfter("id")}, }, { "ObjGetInfoByFd", retError, "info_by_fd", "BPF_OBJ_GET_INFO_BY_FD", []patch{replace(rawPointer, "info")}, }, { "RawTracepointOpen", retFd, "raw_tracepoint_open", "BPF_RAW_TRACEPOINT_OPEN", []patch{replace(stringPointer, "name")}, }, { "BtfLoad", retFd, "btf_load", "BPF_BTF_LOAD", []patch{replace(bytePtr, "btf", "btf_log_buf")}, }, { "LinkCreate", retFd, "link_create", "BPF_LINK_CREATE", []patch{ replace(enumTypes["AttachType"], "attach_type"), choose(4, "target_btf_id"), replace(typeID, "target_btf_id"), }, }, { "LinkCreateIter", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 1), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, replace(rawPointer, "iter_info"), }, }, { "LinkCreatePerfEvent", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 2), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, }, }, { "LinkCreateKprobeMulti", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 3), replace(enumTypes["AttachType"], "attach_type"), modify(func(m *btf.Member) error { return rename("flags", "kprobe_multi_flags")(m.Type.(*btf.Struct)) }, "kprobe_multi"), flattenAnon, replace(uint64Ptr, "cookies"), replace(uintptrPointer, "addrs"), replace(stringSlicePointer, "syms"), rename("cnt", "count"), }, }, { "LinkCreateNetfilter", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 5), replace(enumTypes["AttachType"], "attach_type"), modify(func(m *btf.Member) error { return rename("flags", "netfilter_flags")(m.Type.(*btf.Struct)) }, "netfilter"), flattenAnon, replace(enumTypes["NetfilterProtocolFamily"], "pf"), replace(enumTypes["NetfilterInetHook"], "hooknum"), }, }, { "LinkCreateTracing", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 4), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, replace(btfID, "target_btf_id"), }, }, { "LinkCreateTcx", retFd, "link_create", "BPF_LINK_CREATE", []patch{ choose(1, "target_ifindex"), choose(4, "tcx"), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, flattenAnon, // flatten tcx member rename("relative_fd", "relative_fd_or_id"), }, }, { "LinkCreateUprobeMulti", retFd, "link_create", "BPF_LINK_CREATE", []patch{ chooseNth(4, 7), replace(enumTypes["AttachType"], "attach_type"), modify(func(m *btf.Member) error { return rename("flags", "uprobe_multi_flags")(m.Type.(*btf.Struct)) }, "uprobe_multi"), flattenAnon, replace(stringPointer, "path"), replace(uint64Ptr, "offsets"), replace(uint64Ptr, "ref_ctr_offsets"), replace(uint64Ptr, "cookies"), rename("cnt", "count"), }, }, { "LinkCreateNetkit", retFd, "link_create", "BPF_LINK_CREATE", []patch{ choose(1, "target_ifindex"), choose(4, "netkit"), replace(enumTypes["AttachType"], "attach_type"), flattenAnon, flattenAnon, rename("relative_fd", "relative_fd_or_id"), }, }, { "LinkDetach", retError, "link_detach", "BPF_LINK_DETACH", nil, }, { "LinkUpdate", retError, "link_update", "BPF_LINK_UPDATE", nil, }, { "EnableStats", retFd, "enable_stats", "BPF_ENABLE_STATS", nil, }, { "IterCreate", retFd, "iter_create", "BPF_ITER_CREATE", nil, }, { "ProgQuery", retError, "prog_query", "BPF_PROG_QUERY", []patch{ replace(enumTypes["AttachType"], "attach_type"), replace(progIDPtr, "prog_ids", "prog_attach_flags"), replace(linkIDPtr, "link_ids", "link_attach_flags"), flattenAnon, rename("prog_cnt", "count"), rename("target_fd", "target_fd_or_ifindex"), }, }, } sort.Slice(attrs, func(i, j int) bool { return attrs[i].goType < attrs[j].goType }) var bpfAttr *btf.Union if err := spec.TypeByName("bpf_attr", &bpfAttr); err != nil { return nil, err } attrTypes, err := splitUnion(bpfAttr, types{ {"map_create", "map_type"}, {"map_elem", "map_fd"}, {"map_elem_batch", "batch"}, {"prog_load", "prog_type"}, {"obj_pin", "pathname"}, {"prog_attach", ""}, {"prog_run", "test"}, {"obj_next_id", ""}, {"info_by_fd", "info"}, {"prog_query", "query"}, {"raw_tracepoint_open", "raw_tracepoint"}, {"btf_load", "btf"}, {"task_fd_query", "task_fd_query"}, {"link_create", "link_create"}, {"link_update", "link_update"}, {"link_detach", "link_detach"}, {"enable_stats", "enable_stats"}, {"iter_create", "iter_create"}, {"prog_bind_map", "prog_bind_map"}, }) if err != nil { return nil, fmt.Errorf("split bpf_attr: %w", err) } var nattrs uint32 for _, s := range attrs { debugln("attr", s.goType) t := attrTypes[s.cType] if t == nil { return nil, fmt.Errorf("unknown attr %q", s.cType) } goAttrType := s.goType + "Attr" if err := outputPatchedStruct(gf, w, goAttrType, t, s.patches); err != nil { return nil, fmt.Errorf("output %q: %w", goAttrType, err) } switch s.ret { case retError: fmt.Fprintf(w, "func %s(attr *%s) error { _, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); return err }\n\n", s.goType, goAttrType, s.cmd) case retFd: fmt.Fprintf(w, "func %s(attr *%s) (*FD, error) { fd, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); if err != nil { return nil, err }; return NewFD(int(fd)) }\n\n", s.goType, goAttrType, s.cmd) } nattrs++ } fmt.Printf("Generated %d attrs\n", nattrs) // Link info type specific linkInfoExtraTypes := []struct { goType string patches []patch }{ {"CgroupLinkInfo", []patch{ choose(3, "cgroup"), flattenAnon, replace(enumTypes["AttachType"], "attach_type"), }, }, {"IterLinkInfo", []patch{ choose(3, "iter"), flattenAnon, replace(bytePtr, "target_name"), truncateAfter("target_name_len"), }, }, {"NetNsLinkInfo", []patch{choose(3, "netns"), flattenAnon, replace(enumTypes["AttachType"], "attach_type"), }, }, {"RawTracepointLinkInfo", []patch{choose(3, "raw_tracepoint"), flattenAnon, replace(bytePtr, "tp_name"), }, }, {"TracingLinkInfo", []patch{ choose(3, "tracing"), flattenAnon, replace(enumTypes["AttachType"], "attach_type"), replace(typeID, "target_btf_id"), }, }, {"XDPLinkInfo", []patch{choose(3, "xdp"), flattenAnon, }, }, {"TcxLinkInfo", []patch{ choose(3, "tcx"), flattenAnon, replace(enumTypes["AttachType"], "attach_type"), }, }, {"NetfilterLinkInfo", []patch{ choose(3, "netfilter"), flattenAnon, replace(enumTypes["NetfilterProtocolFamily"], "pf"), replace(enumTypes["NetfilterInetHook"], "hooknum"), }, }, {"NetkitLinkInfo", []patch{ choose(3, "netkit"), flattenAnon, replace(enumTypes["AttachType"], "attach_type"), }, }, {"KprobeMultiLinkInfo", []patch{ choose(3, "kprobe_multi"), flattenAnon, replace(uint64Ptr, "addrs"), replace(uint64Ptr, "cookies"), }, }, {"UprobeMultiLinkInfo", []patch{ choose(3, "uprobe_multi"), flattenAnon, replace(bytePtr, "path"), replace(uint64Ptr, "offsets"), replace(uint64Ptr, "ref_ctr_offsets"), replace(uint64Ptr, "cookies"), }, }, {"PerfEventLinkInfo", []patch{ choose(3, "perf_event"), flattenAnon, renameNth(3, "perf_event_type"), replace(enumTypes["PerfEventType"], "perf_event_type"), truncateAfter("perf_event_type"), }, }, {"KprobeLinkInfo", []patch{ choose(3, "perf_event"), flattenAnon, renameNth(3, "perf_event_type"), replace(enumTypes["PerfEventType"], "perf_event_type"), choose(4, "kprobe"), flattenAnon, replace(bytePtr, "func_name"), }, }, {"UprobeLinkInfo", []patch{ choose(3, "perf_event"), flattenAnon, renameNth(3, "perf_event_type"), replace(enumTypes["PerfEventType"], "perf_event_type"), choose(4, "uprobe"), flattenAnon, replace(bytePtr, "file_name"), }, }, {"TracepointLinkInfo", []patch{ choose(3, "perf_event"), flattenAnon, renameNth(3, "perf_event_type"), replace(enumTypes["PerfEventType"], "perf_event_type"), choose(4, "tracepoint"), flattenAnon, replace(bytePtr, "tp_name"), }, }, {"EventLinkInfo", []patch{ choose(3, "perf_event"), flattenAnon, renameNth(3, "perf_event_type"), replace(enumTypes["PerfEventType"], "perf_event_type"), choose(4, "event"), flattenAnon, renameNth(5, "event_type"), }, }, } sort.Slice(linkInfoExtraTypes, func(i, j int) bool { return linkInfoExtraTypes[i].goType < linkInfoExtraTypes[j].goType }) var bpfLinkInfo *btf.Struct if err := spec.TypeByName("bpf_link_info", &bpfLinkInfo); err != nil { return nil, err } patches := []patch{ replace(enumTypes["LinkType"], "type"), replace(linkID, "id"), } for _, s := range linkInfoExtraTypes { if err := outputPatchedStruct(gf, w, s.goType, bpfLinkInfo, append(patches, s.patches...)); err != nil { return nil, fmt.Errorf("output %q: %w", s.goType, err) } } return w.Bytes(), nil } type failedPatchError struct { Type *btf.Struct number int err error } func (fpe *failedPatchError) Unwrap() error { return fpe.err } func (fpe *failedPatchError) Error() string { return fmt.Sprintf("patch %d: %v", fpe.number, fpe.err) } func outputPatchedStruct(gf *btf.GoFormatter, w *bytes.Buffer, id string, s *btf.Struct, patches []patch) error { s = btf.Copy(s).(*btf.Struct) for i, p := range patches { if err := p(s); err != nil { return &failedPatchError{s, i, err} } } decl, err := gf.TypeDeclaration(id, s) if err != nil { return err } w.WriteString(decl) w.WriteString("\n\n") return nil } type types []struct { name string cFieldOrFirstMember string } func splitUnion(union *btf.Union, types types) (map[string]*btf.Struct, error) { structs := make(map[string]*btf.Struct) for i, t := range types { member := union.Members[i] s, ok := member.Type.(*btf.Struct) if !ok { return nil, fmt.Errorf("%q: %s is not a struct", t.name, member.Type) } if member.Name == "" { // This is an anonymous struct, check the name of the first member instead. if name := s.Members[0].Name; name != t.cFieldOrFirstMember { return nil, fmt.Errorf("first field of %q is %q, not %q", t.name, name, t.cFieldOrFirstMember) } } else if member.Name != t.cFieldOrFirstMember { return nil, fmt.Errorf("name for %q is %q, not %q", t.name, member.Name, t.cFieldOrFirstMember) } structs[t.name] = s } return structs, nil } type patch func(*btf.Struct) error func modify(fn func(*btf.Member) error, members ...string) patch { return func(s *btf.Struct) error { want := make(map[string]bool) for _, name := range members { want[name] = true } for i, m := range s.Members { if want[m.Name] { if err := fn(&s.Members[i]); err != nil { return err } delete(want, m.Name) } } if len(want) == 0 { return nil } var missing []string for name := range want { missing = append(missing, name) } sort.Strings(missing) return fmt.Errorf("missing members: %v", strings.Join(missing, ", ")) } } func modifyNth(fn func(*btf.Member) error, indices ...int) patch { return func(s *btf.Struct) error { for _, i := range indices { if i >= len(s.Members) { return fmt.Errorf("index %d is out of bounds", i) } if err := fn(&s.Members[i]); err != nil { return fmt.Errorf("member #%d: %w", i, err) } } return nil } } func replace(t btf.Type, members ...string) patch { return modify(func(m *btf.Member) error { m.Type = t return nil }, members...) } func choose(member int, name string) patch { return modifyNth(func(m *btf.Member) error { union, ok := m.Type.(*btf.Union) if !ok { return fmt.Errorf("member %d is %s, not a union", member, m.Type) } for _, um := range union.Members { if um.Name == name { m.Name = um.Name m.Type = um.Type return nil } } return fmt.Errorf("%s has no member %q", union, name) }, member) } func chooseNth(member int, n int) patch { return modifyNth(func(m *btf.Member) error { union, ok := m.Type.(*btf.Union) if !ok { return fmt.Errorf("member %d is %s, not a union", member, m.Type) } if n >= len(union.Members) { return fmt.Errorf("member %d is out of bounds", n) } um := union.Members[n] m.Name = um.Name m.Type = um.Type return nil }, member) } func flattenAnon(s *btf.Struct) error { for i := range s.Members { m := &s.Members[i] if m.Type.TypeName() != "" { continue } var newMembers []btf.Member switch cs := m.Type.(type) { case *btf.Struct: for j := range cs.Members { cs.Members[j].Offset += m.Offset } newMembers = cs.Members case *btf.Union: cs.Members[0].Offset += m.Offset newMembers = []btf.Member{cs.Members[0]} default: continue } s.Members = slices.Replace(s.Members, i, i+1, newMembers...) } return nil } func truncateAfter(name string) patch { return func(s *btf.Struct) error { for i, m := range s.Members { if m.Name != name { continue } size, err := btf.Sizeof(m.Type) if err != nil { return err } s.Members = s.Members[:i+1] s.Size = m.Offset.Bytes() + uint32(size) return nil } return fmt.Errorf("no member %q", name) } } func rename(from, to string) patch { return func(s *btf.Struct) error { for i, m := range s.Members { if m.Name == from { s.Members[i].Name = to return nil } } return fmt.Errorf("no member named %q", from) } } func renameNth(idx int, to string) patch { return func(s *btf.Struct) error { if idx >= len(s.Members) { return fmt.Errorf("index %d is out of bounds", idx) } s.Members[idx].Name = to return nil } } func name(member int, name string) patch { return modifyNth(func(m *btf.Member) error { if m.Name != "" { return fmt.Errorf("member already has name %q", m.Name) } m.Name = name return nil }, member) } func replaceWithBytes(members ...string) patch { return modify(func(m *btf.Member) error { if m.BitfieldSize != 0 { return errors.New("replaceWithBytes: member is a bitfield") } size, err := btf.Sizeof(m.Type) if err != nil { return fmt.Errorf("replaceWithBytes: size of %s: %w", m.Type, err) } m.Type = &btf.Array{ Type: &btf.Int{Size: 1}, Nelems: uint32(size), } return nil }, members...) } func remove(member string) patch { return func(s *btf.Struct) error { for i, m := range s.Members { if m.Name == member { s.Members = slices.Delete(s.Members, i, i+1) return nil } } return fmt.Errorf("member %q not found", member) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/cmd/genwinfunctions.awk000077500000000000000000000016441520243672000255120ustar00rootroot00000000000000#!/usr/bin/gawk -f # Generate constants from eBPF for Windows headers. # # This script expects include/ebpf_structs.h as input. BEGIN { print "// Code generated by internal/cmd/genwinfunctions.awk; DO NOT EDIT." print "" print "package asm" print "" print "// Code in this file is derived from eBPF for Windows, available under the MIT License." print "" print "import \"github.com/cilium/ebpf/internal/platform\"" print "" print "// Built-in functions (Windows)." print "const (" } /BPF_FUNC_[[:alnum:]_]+ *= *[0-9]+,/ { name = gensub(/.*BPF_FUNC_([[:alnum:]_]+) *=.*/, "\\1", 1) id = gensub(/.*BPF_FUNC_[[:alnum:]_]+ *= *([0-9]+),.*/, "\\1", 1) split(tolower(name), parts, "_") result = "WindowsFn" for (i in parts) { part = parts[i] result = result substr(toupper(substr(part,1,1)), 1, 1) substr(part, 2) } print "\t" result " = BuiltinFunc(platform.WindowsTag | " id ")" } END { print ")" print "" } golang-github-cilium-ebpf-0.21.0+ds1/internal/deque.go000066400000000000000000000032771520243672000224560ustar00rootroot00000000000000package internal import "math/bits" // Deque implements a double ended queue. type Deque[T any] struct { elems []T read, write uint64 mask uint64 } // Reset clears the contents of the deque while retaining the backing buffer. func (dq *Deque[T]) Reset() { var zero T for i := dq.read; i < dq.write; i++ { dq.elems[i&dq.mask] = zero } dq.read, dq.write = 0, 0 } func (dq *Deque[T]) Empty() bool { return dq.read == dq.write } // Push adds an element to the end. func (dq *Deque[T]) Push(e T) { dq.Grow(1) dq.elems[dq.write&dq.mask] = e dq.write++ } // Shift returns the first element or the zero value. func (dq *Deque[T]) Shift() T { var zero T if dq.Empty() { return zero } index := dq.read & dq.mask t := dq.elems[index] dq.elems[index] = zero dq.read++ return t } // Pop returns the last element or the zero value. func (dq *Deque[T]) Pop() T { var zero T if dq.Empty() { return zero } dq.write-- index := dq.write & dq.mask t := dq.elems[index] dq.elems[index] = zero return t } // Grow the deque's capacity, if necessary, to guarantee space for another n // elements. func (dq *Deque[T]) Grow(n int) { have := dq.write - dq.read need := have + uint64(n) if need < have { panic("overflow") } if uint64(len(dq.elems)) >= need { return } // Round up to the new power of two which is at least 8. // See https://jameshfisher.com/2018/03/30/round-up-power-2/ capacity := max(1<<(64-bits.LeadingZeros64(need-1)), 8) elems := make([]T, have, capacity) pivot := dq.read & dq.mask copied := copy(elems, dq.elems[pivot:]) copy(elems[copied:], dq.elems[:pivot]) dq.elems = elems[:capacity] dq.mask = uint64(capacity) - 1 dq.read, dq.write = 0, have } golang-github-cilium-ebpf-0.21.0+ds1/internal/deque_test.go000066400000000000000000000025111520243672000235030ustar00rootroot00000000000000package internal import "testing" func TestDeque(t *testing.T) { t.Run("pop", func(t *testing.T) { var dq Deque[int] dq.Push(1) dq.Push(2) if dq.Pop() != 2 { t.Error("Didn't pop 2 first") } if dq.Pop() != 1 { t.Error("Didn't pop 1 second") } if dq.Pop() != 0 { t.Error("Didn't pop zero") } }) t.Run("shift", func(t *testing.T) { var td Deque[int] td.Push(1) td.Push(2) if td.Shift() != 1 { t.Error("Didn't shift 1 first") } if td.Shift() != 2 { t.Error("Didn't shift b second") } if td.Shift() != 0 { t.Error("Didn't shift zero") } }) t.Run("push", func(t *testing.T) { var td Deque[int] td.Push(1) td.Push(2) td.Shift() for i := 1; i <= 12; i++ { td.Push(i) } if td.Shift() != 2 { t.Error("Didn't shift 2 first") } for i := 1; i <= 12; i++ { if v := td.Shift(); v != i { t.Fatalf("Shifted %d at pos %d", v, i) } } }) t.Run("grow", func(t *testing.T) { var td Deque[int] td.Push(1) td.Push(2) td.Push(3) td.Shift() td.Grow(7) if len(td.elems) < 9 { t.Fatal("Expected at least 9 elements, got", len(td.elems)) } if cap(td.elems)&(cap(td.elems)-1) != 0 { t.Fatalf("Capacity %d is not a power of two", cap(td.elems)) } if td.Shift() != 2 || td.Shift() != 3 { t.Fatal("Elements don't match after grow") } }) } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/000077500000000000000000000000001520243672000215745ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/enums.go000066400000000000000000000031021520243672000232460ustar00rootroot00000000000000//go:build windows package efw import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) /* Converts an attach type enum into a GUID. ebpf_result_t ebpf_get_ebpf_attach_type( bpf_attach_type_t bpf_attach_type, _Out_ ebpf_attach_type_t* ebpf_attach_type_t *ebpf_attach_type) */ var ebpfGetEbpfAttachTypeProc = newProc("ebpf_get_ebpf_attach_type") func EbpfGetEbpfAttachType(attachType uint32) (windows.GUID, error) { addr, err := ebpfGetEbpfAttachTypeProc.Find() if err != nil { return windows.GUID{}, err } var attachTypeGUID windows.GUID err = errorResult(syscall.SyscallN(addr, uintptr(attachType), uintptr(unsafe.Pointer(&attachTypeGUID)), )) return attachTypeGUID, err } /* Retrieve a program type given a GUID. bpf_prog_type_t ebpf_get_bpf_program_type(_In_ const ebpf_program_type_t* program_type) */ var ebpfGetBpfProgramTypeProc = newProc("ebpf_get_bpf_program_type") func EbpfGetBpfProgramType(programType windows.GUID) (uint32, error) { addr, err := ebpfGetBpfProgramTypeProc.Find() if err != nil { return 0, err } return uint32Result(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&programType)))), nil } /* Retrieve an attach type given a GUID. bpf_attach_type_t ebpf_get_bpf_attach_type(_In_ const ebpf_attach_type_t* ebpf_attach_type) */ var ebpfGetBpfAttachTypeProc = newProc("ebpf_get_bpf_attach_type") func EbpfGetBpfAttachType(attachType windows.GUID) (uint32, error) { addr, err := ebpfGetBpfAttachTypeProc.Find() if err != nil { return 0, err } return uint32Result(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&attachType)))), nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/error_reporting.go000066400000000000000000000102201520243672000253400ustar00rootroot00000000000000//go:build windows package efw import ( "errors" "fmt" "os" "syscall" "testing" "golang.org/x/sys/windows" ) func init() { if !testing.Testing() { return } if isDebuggerPresent() { return } if err := configureCRTErrorReporting(); err != nil { fmt.Fprintln(os.Stderr, "WARNING: Could not configure CRT error reporting, tests may hang:", err) } } var errErrorReportingAlreadyConfigured = errors.New("error reporting already configured") // Configure built-in error reporting of the C runtime library. // // The C runtime emits assertion failures into a graphical message box by default. // This causes a hang in CI environments. This function configures the CRT to // log to stderr instead. func configureCRTErrorReporting() error { const ucrtDebug = "ucrtbased.dll" // Constants from crtdbg.h // // See https://doxygen.reactos.org/da/d40/crt_2crtdbg_8h_source.html const ( _CRT_ERROR = 1 _CRT_ASSERT = 2 _CRTDBG_MODE_FILE = 0x1 _CRTDBG_MODE_WNDW = 0x4 _CRTDBG_HFILE_ERROR = -2 _CRTDBG_FILE_STDERR = -4 ) // Load the efW API to trigger loading the CRT. This may fail, in which case // we can't figure out which CRT is being used. // In that case we rely on the error bubbling up via some other path. _ = module.Load() ucrtHandle, err := syscall.UTF16PtrFromString(ucrtDebug) if err != nil { return err } var handle windows.Handle err = windows.GetModuleHandleEx(0, ucrtHandle, &handle) if errors.Is(err, windows.ERROR_MOD_NOT_FOUND) { // Loading the ebpf api did not pull in the debug UCRT, so there is // nothing to configure. return nil } else if err != nil { return err } defer windows.FreeLibrary(handle) setReportModeAddr, err := windows.GetProcAddress(handle, "_CrtSetReportMode") if err != nil { return err } setReportMode := func(reportType int, reportMode int) (int, error) { // See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetreportmode?view=msvc-170 r1, _, err := syscall.SyscallN(setReportModeAddr, uintptr(reportType), uintptr(reportMode)) if int(r1) == -1 { return 0, fmt.Errorf("set report mode for type %d: %w", reportType, err) } return int(r1), nil } setReportFileAddr, err := windows.GetProcAddress(handle, "_CrtSetReportFile") if err != nil { return err } setReportFile := func(reportType int, reportFile int) (int, error) { // See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetreportfile?view=msvc-170 r1, _, err := syscall.SyscallN(setReportFileAddr, uintptr(reportType), uintptr(reportFile)) if int(r1) == _CRTDBG_HFILE_ERROR { return 0, fmt.Errorf("set report file for type %d: %w", reportType, err) } return int(r1), nil } reportToFile := func(reportType, defaultMode int) error { oldMode, err := setReportMode(reportType, _CRTDBG_MODE_FILE) if err != nil { return err } if oldMode != defaultMode { // Attempt to restore old mode if it was different from the expected default. _, _ = setReportMode(reportType, oldMode) return errErrorReportingAlreadyConfigured } oldFile, err := setReportFile(reportType, _CRTDBG_FILE_STDERR) if err != nil { return err } if oldFile != -1 { // Attempt to restore old file if it was different from the expected default. _, _ = setReportFile(reportType, oldFile) return errErrorReportingAlreadyConfigured } return nil } // See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetreportmode?view=msvc-170#remarks // for defaults. if err := reportToFile(_CRT_ASSERT, _CRTDBG_MODE_WNDW); err != nil { return err } if err := reportToFile(_CRT_ERROR, _CRTDBG_MODE_WNDW); err != nil { return err } return nil } // isDebuggerPresent returns true if the current process is being debugged. // // See https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent func isDebuggerPresent() bool { kernel32Handle, err := windows.LoadLibrary("kernel32.dll") if err != nil { return false } isDebuggerPresentAddr, err := windows.GetProcAddress(kernel32Handle, "IsDebuggerPresent") if err != nil { return false } r1, _, _ := syscall.SyscallN(isDebuggerPresentAddr) return r1 != 0 } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/error_reporting_test.go000066400000000000000000000005031520243672000264020ustar00rootroot00000000000000//go:build windows package efw import ( "testing" "github.com/go-quicktest/qt" ) func TestConfigureErrorReporting(t *testing.T) { qt.Assert(t, qt.ErrorIs(configureCRTErrorReporting(), errErrorReportingAlreadyConfigured)) } func TestIsDebuggerPresent(t *testing.T) { qt.Assert(t, qt.IsFalse(isDebuggerPresent())) } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/fd.go000066400000000000000000000012511520243672000225130ustar00rootroot00000000000000//go:build windows package efw import ( "syscall" "unsafe" ) // ebpf_result_t ebpf_close_fd(fd_t fd) var ebpfCloseFdProc = newProc("ebpf_close_fd") func EbpfCloseFd(fd int) error { addr, err := ebpfCloseFdProc.Find() if err != nil { return err } return errorResult(syscall.SyscallN(addr, uintptr(fd))) } // ebpf_result_t ebpf_duplicate_fd(fd_t fd, _Out_ fd_t* dup) var ebpfDuplicateFdProc = newProc("ebpf_duplicate_fd") func EbpfDuplicateFd(fd int) (int, error) { addr, err := ebpfDuplicateFdProc.Find() if err != nil { return -1, err } var dup FD err = errorResult(syscall.SyscallN(addr, uintptr(fd), uintptr(unsafe.Pointer(&dup)))) return int(dup), err } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/map.go000066400000000000000000000046701520243672000227070ustar00rootroot00000000000000//go:build windows package efw import ( "runtime" "syscall" "unsafe" "golang.org/x/sys/windows" ) /* ebpf_ring_buffer_map_map_buffer( fd_t map_fd, _Outptr_result_maybenull_ void** consumer, _Outptr_result_maybenull_ const void** producer, _Outptr_result_buffer_maybenull_(*data_size) const uint8_t** data, _Out_ size_t* data_size) EBPF_NO_EXCEPT; */ var ebpfRingBufferMapMapBufferProc = newProc("ebpf_ring_buffer_map_map_buffer") func EbpfRingBufferMapMapBuffer(mapFd int) (consumer, producer, data *uint8, dataLen Size, _ error) { addr, err := ebpfRingBufferMapMapBufferProc.Find() if err != nil { return nil, nil, nil, 0, err } err = errorResult(syscall.SyscallN(addr, uintptr(mapFd), uintptr(unsafe.Pointer(&consumer)), uintptr(unsafe.Pointer(&producer)), uintptr(unsafe.Pointer(&data)), uintptr(unsafe.Pointer(&dataLen)), )) if err != nil { return nil, nil, nil, 0, err } return consumer, producer, data, dataLen, nil } /* ebpf_ring_buffer_map_unmap_buffer( fd_t map_fd, _In_ void* consumer, _In_ const void* producer, _In_ const void* data) EBPF_NO_EXCEPT; */ var ebpfRingBufferMapUnmapBufferProc = newProc("ebpf_ring_buffer_map_unmap_buffer") func EbpfRingBufferMapUnmapBuffer(mapFd int, consumer, producer, data *uint8) error { addr, err := ebpfRingBufferMapUnmapBufferProc.Find() if err != nil { return err } return errorResult(syscall.SyscallN(addr, uintptr(mapFd), uintptr(unsafe.Pointer(consumer)), uintptr(unsafe.Pointer(producer)), uintptr(unsafe.Pointer(data)), )) } /* ebpf_result_t ebpf_map_set_wait_handle( fd_t map_fd, uint64_t index, ebpf_handle_t handle) */ var ebpfMapSetWaitHandleProc = newProc("ebpf_map_set_wait_handle") func EbpfMapSetWaitHandle(mapFd int, index uint64, handle windows.Handle) error { addr, err := ebpfMapSetWaitHandleProc.Find() if err != nil { return err } return errorResult(syscall.SyscallN(addr, uintptr(mapFd), uintptr(index), uintptr(handle), )) } /* ebpf_result_t ebpf_ring_buffer_map_write( fd_t ring_buffer_map_fd, const void* data, size_t data_length) */ var ebpfRingBufferMapWriteProc = newProc("ebpf_ring_buffer_map_write") func EbpfRingBufferMapWrite(ringBufferMapFd int, data []byte) error { addr, err := ebpfRingBufferMapWriteProc.Find() if err != nil { return err } err = errorResult(syscall.SyscallN(addr, uintptr(ringBufferMapFd), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)), )) runtime.KeepAlive(data) return err } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/module.go000066400000000000000000000017101520243672000234070ustar00rootroot00000000000000//go:build windows // Package efw contains support code for eBPF for Windows. package efw import ( "golang.org/x/sys/windows" ) // module is the global handle for the eBPF for Windows user-space API. var module = windows.NewLazyDLL("ebpfapi.dll") // FD is the equivalent of fd_t. // // See https://github.com/microsoft/ebpf-for-windows/blob/54632eb360c560ebef2f173be1a4a4625d540744/include/ebpf_api.h#L24 type FD int32 // Size is the equivalent of size_t. // // This is correct on amd64 and arm64 according to tests on godbolt.org. type Size uint64 // Int is the equivalent of int on MSVC (am64, arm64) and MinGW (gcc, clang). type Int int32 // ObjectType is the equivalent of ebpf_object_type_t. // // See https://github.com/microsoft/ebpf-for-windows/blob/44f5de09ec0f3f7ad176c00a290c1cb7106cdd5e/include/ebpf_core_structs.h#L41 type ObjectType uint32 const ( EBPF_OBJECT_UNKNOWN ObjectType = iota EBPF_OBJECT_MAP EBPF_OBJECT_LINK EBPF_OBJECT_PROGRAM ) golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/native.go000066400000000000000000000021141520243672000234070ustar00rootroot00000000000000//go:build windows package efw import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) /* ebpf_result_t ebpf_object_load_native_by_fds( _In_z_ const char* file_name, _Inout_ size_t* count_of_maps, _Out_writes_opt_(count_of_maps) fd_t* map_fds, _Inout_ size_t* count_of_programs, _Out_writes_opt_(count_of_programs) fd_t* program_fds) */ var ebpfObjectLoadNativeByFdsProc = newProc("ebpf_object_load_native_by_fds") func EbpfObjectLoadNativeFds(fileName string, mapFds []FD, programFds []FD) (int, int, error) { addr, err := ebpfObjectLoadNativeByFdsProc.Find() if err != nil { return 0, 0, err } fileBytes, err := windows.ByteSliceFromString(fileName) if err != nil { return 0, 0, err } countOfMaps := Size(len(mapFds)) countOfPrograms := Size(len(programFds)) err = errorResult(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&fileBytes[0])), uintptr(unsafe.Pointer(&countOfMaps)), uintptr(unsafe.Pointer(&mapFds[0])), uintptr(unsafe.Pointer(&countOfPrograms)), uintptr(unsafe.Pointer(&programFds[0])), )) return int(countOfMaps), int(countOfPrograms), err } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/object.go000066400000000000000000000057441520243672000234030ustar00rootroot00000000000000//go:build windows package efw import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) // https://github.com/microsoft/ebpf-for-windows/blob/9d9003c39c3fd75be5225ac0fce30077d6bf0604/include/ebpf_core_structs.h#L15 const _EBPF_MAX_PIN_PATH_LENGTH = 256 /* Retrieve object info and type from a fd. ebpf_result_t ebpf_object_get_info_by_fd( fd_t bpf_fd, _Inout_updates_bytes_to_opt_(*info_size, *info_size) void* info, _Inout_opt_ uint32_t* info_size, _Out_opt_ ebpf_object_type_t* type) */ var ebpfObjectGetInfoByFdProc = newProc("ebpf_object_get_info_by_fd") func EbpfObjectGetInfoByFd(fd int, info unsafe.Pointer, info_size *uint32) (ObjectType, error) { addr, err := ebpfObjectGetInfoByFdProc.Find() if err != nil { return 0, err } var objectType ObjectType err = errorResult(syscall.SyscallN(addr, uintptr(fd), uintptr(info), uintptr(unsafe.Pointer(info_size)), uintptr(unsafe.Pointer(&objectType)), )) return objectType, err } // ebpf_result_t ebpf_object_unpin(_In_z_ const char* path) var ebpfObjectUnpinProc = newProc("ebpf_object_unpin") func EbpfObjectUnpin(path string) error { addr, err := ebpfObjectUnpinProc.Find() if err != nil { return err } pathBytes, err := windows.ByteSliceFromString(path) if err != nil { return err } return errorResult(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&pathBytes[0])))) } /* Retrieve the next pinned object path. ebpf_result_t ebpf_get_next_pinned_object_path( _In_opt_z_ const char* start_path, _Out_writes_z_(next_path_len) char* next_path, size_t next_path_len, _Inout_opt_ ebpf_object_type_t* type) */ var ebpfGetNextPinnedObjectPath = newProc("ebpf_get_next_pinned_object_path") func EbpfGetNextPinnedObjectPath(startPath string, objectType ObjectType) (string, ObjectType, error) { addr, err := ebpfGetNextPinnedObjectPath.Find() if err != nil { return "", 0, err } ptr, err := windows.BytePtrFromString(startPath) if err != nil { return "", 0, err } tmp := make([]byte, _EBPF_MAX_PIN_PATH_LENGTH) err = errorResult(syscall.SyscallN(addr, uintptr(unsafe.Pointer(ptr)), uintptr(unsafe.Pointer(&tmp[0])), uintptr(len(tmp)), uintptr(unsafe.Pointer(&objectType)), )) return windows.ByteSliceToString(tmp), objectType, err } /* Canonicalize a path using filesystem canonicalization rules. _Must_inspect_result_ ebpf_result_t ebpf_canonicalize_pin_path(_Out_writes_(output_size) char* output, size_t output_size, _In_z_ const char* input) */ var ebpfCanonicalizePinPath = newProc("ebpf_canonicalize_pin_path") func EbpfCanonicalizePinPath(input string) (string, error) { addr, err := ebpfCanonicalizePinPath.Find() if err != nil { return "", err } inputBytes, err := windows.ByteSliceFromString(input) if err != nil { return "", err } output := make([]byte, _EBPF_MAX_PIN_PATH_LENGTH) err = errorResult(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&output[0])), uintptr(len(output)), uintptr(unsafe.Pointer(&inputBytes[0])), )) return windows.ByteSliceToString(output), err } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/proc.go000066400000000000000000000017571520243672000231000ustar00rootroot00000000000000//go:build windows package efw import ( "errors" "fmt" "syscall" "golang.org/x/sys/windows" ) /* The BPF syscall wrapper which is ABI compatible with Linux. int bpf(int cmd, union bpf_attr* attr, unsigned int size) */ var BPF = newProc("bpf") type proc struct { proc *windows.LazyProc } func newProc(name string) proc { return proc{module.NewProc(name)} } func (p proc) Find() (uintptr, error) { if err := p.proc.Find(); err != nil { if errors.Is(err, windows.ERROR_MOD_NOT_FOUND) { return 0, fmt.Errorf("load %s: not found", module.Name) } return 0, err } return p.proc.Addr(), nil } // uint32Result wraps a function which returns a uint32_t. func uint32Result(r1, _ uintptr, _ syscall.Errno) uint32 { return uint32(r1) } // errorResult wraps a function which returns ebpf_result_t. func errorResult(r1, _ uintptr, errNo syscall.Errno) error { err := resultToError(Result(r1)) if err != nil && errNo != 0 { return fmt.Errorf("%w (errno: %v)", err, errNo) } return err } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/proc_test.go000066400000000000000000000007061520243672000241300ustar00rootroot00000000000000//go:build windows package efw import ( "testing" "github.com/go-quicktest/qt" ) func TestNewProc(t *testing.T) { _, err := newProc("a_function_which_doesnt_exist").Find() qt.Assert(t, qt.ErrorMatches(err, ".* a_function_which_doesnt_exist .*")) } func TestCall(t *testing.T) { var err error allocs := testing.AllocsPerRun(10, func() { _, err = EbpfGetEbpfAttachType(2) }) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(allocs, 0)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/program.go000066400000000000000000000015071520243672000235750ustar00rootroot00000000000000//go:build windows package efw import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) /* Attach a program. ebpf_result_t ebpf_program_attach_by_fds( fd_t program_fd, _In_opt_ const ebpf_attach_type_t* attach_type, _In_reads_bytes_opt_(attach_parameters_size) void* attach_parameters, size_t attach_parameters_size, _Out_ fd_t* link) */ var ebpfProgramAttachByFdsProc = newProc("ebpf_program_attach_by_fds") func EbpfProgramAttachFds(fd int, attachType windows.GUID, params unsafe.Pointer, params_size uintptr) (int, error) { addr, err := ebpfProgramAttachByFdsProc.Find() if err != nil { return 0, err } var link FD err = errorResult(syscall.SyscallN(addr, uintptr(fd), uintptr(unsafe.Pointer(&attachType)), uintptr(params), params_size, uintptr(unsafe.Pointer(&link)), )) return int(link), err } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/result.go000066400000000000000000000022071520243672000234420ustar00rootroot00000000000000//go:build windows package efw // See https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_result.h type Result int32 //go:generate go tool stringer -tags windows -output result_string_windows.go -type=Result const ( EBPF_SUCCESS Result = iota EBPF_VERIFICATION_FAILED EBPF_JIT_COMPILATION_FAILED EBPF_PROGRAM_LOAD_FAILED EBPF_INVALID_FD EBPF_INVALID_OBJECT EBPF_INVALID_ARGUMENT EBPF_OBJECT_NOT_FOUND EBPF_OBJECT_ALREADY_EXISTS EBPF_FILE_NOT_FOUND EBPF_ALREADY_PINNED EBPF_NOT_PINNED EBPF_NO_MEMORY EBPF_PROGRAM_TOO_LARGE EBPF_RPC_EXCEPTION EBPF_ALREADY_INITIALIZED EBPF_ELF_PARSING_FAILED EBPF_FAILED EBPF_OPERATION_NOT_SUPPORTED EBPF_KEY_NOT_FOUND EBPF_ACCESS_DENIED EBPF_BLOCKED_BY_POLICY EBPF_ARITHMETIC_OVERFLOW EBPF_EXTENSION_FAILED_TO_LOAD EBPF_INSUFFICIENT_BUFFER EBPF_NO_MORE_KEYS EBPF_KEY_ALREADY_EXISTS EBPF_NO_MORE_TAIL_CALLS EBPF_PENDING EBPF_OUT_OF_SPACE EBPF_CANCELED EBPF_INVALID_POINTER EBPF_TIMEOUT EBPF_STALE_ID EBPF_INVALID_STATE ) func (r Result) Error() string { return r.String() } func resultToError(res Result) error { if res == EBPF_SUCCESS { return nil } return res } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/result_string_windows.go000066400000000000000000000047511520243672000266100ustar00rootroot00000000000000// Code generated by "stringer -tags windows -output result_string_windows.go -type=Result"; DO NOT EDIT. package efw import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[EBPF_SUCCESS-0] _ = x[EBPF_VERIFICATION_FAILED-1] _ = x[EBPF_JIT_COMPILATION_FAILED-2] _ = x[EBPF_PROGRAM_LOAD_FAILED-3] _ = x[EBPF_INVALID_FD-4] _ = x[EBPF_INVALID_OBJECT-5] _ = x[EBPF_INVALID_ARGUMENT-6] _ = x[EBPF_OBJECT_NOT_FOUND-7] _ = x[EBPF_OBJECT_ALREADY_EXISTS-8] _ = x[EBPF_FILE_NOT_FOUND-9] _ = x[EBPF_ALREADY_PINNED-10] _ = x[EBPF_NOT_PINNED-11] _ = x[EBPF_NO_MEMORY-12] _ = x[EBPF_PROGRAM_TOO_LARGE-13] _ = x[EBPF_RPC_EXCEPTION-14] _ = x[EBPF_ALREADY_INITIALIZED-15] _ = x[EBPF_ELF_PARSING_FAILED-16] _ = x[EBPF_FAILED-17] _ = x[EBPF_OPERATION_NOT_SUPPORTED-18] _ = x[EBPF_KEY_NOT_FOUND-19] _ = x[EBPF_ACCESS_DENIED-20] _ = x[EBPF_BLOCKED_BY_POLICY-21] _ = x[EBPF_ARITHMETIC_OVERFLOW-22] _ = x[EBPF_EXTENSION_FAILED_TO_LOAD-23] _ = x[EBPF_INSUFFICIENT_BUFFER-24] _ = x[EBPF_NO_MORE_KEYS-25] _ = x[EBPF_KEY_ALREADY_EXISTS-26] _ = x[EBPF_NO_MORE_TAIL_CALLS-27] _ = x[EBPF_PENDING-28] _ = x[EBPF_OUT_OF_SPACE-29] _ = x[EBPF_CANCELED-30] _ = x[EBPF_INVALID_POINTER-31] _ = x[EBPF_TIMEOUT-32] _ = x[EBPF_STALE_ID-33] _ = x[EBPF_INVALID_STATE-34] } const _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" var _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} func (i Result) String() string { if i < 0 || i >= Result(len(_Result_index)-1) { return "Result(" + strconv.FormatInt(int64(i), 10) + ")" } return _Result_name[_Result_index[i]:_Result_index[i+1]] } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/result_test.go000066400000000000000000000010661520243672000245030ustar00rootroot00000000000000//go:build windows package efw import ( "testing" "github.com/go-quicktest/qt" ) func TestResultToError(t *testing.T) { qt.Assert(t, qt.IsNil(resultToError(EBPF_SUCCESS))) qt.Assert(t, qt.IsNotNil(resultToError(EBPF_ACCESS_DENIED))) // Ensure that common results do not allocate. for _, result := range []Result{ EBPF_SUCCESS, EBPF_NO_MORE_KEYS, EBPF_KEY_NOT_FOUND, } { t.Run(result.String(), func(t *testing.T) { allocs := testing.AllocsPerRun(1, func() { _ = resultToError(result) }) qt.Assert(t, qt.Equals(allocs, 0.0)) }) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/efw/structs.go000066400000000000000000000030231520243672000236300ustar00rootroot00000000000000//go:build windows package efw import "golang.org/x/sys/windows" // https://github.com/microsoft/ebpf-for-windows/blob/95267a53b26c68a94145d1731e2a4c8b546034c3/include/ebpf_structs.h#L366 const _BPF_OBJ_NAME_LEN = 64 // See https://github.com/microsoft/ebpf-for-windows/blob/95267a53b26c68a94145d1731e2a4c8b546034c3/include/ebpf_structs.h#L372-L386 type BpfMapInfo struct { _ uint32 ///< Map ID. _ uint32 ///< Type of map. _ uint32 ///< Size in bytes of a map key. _ uint32 ///< Size in bytes of a map value. _ uint32 ///< Maximum number of entries allowed in the map. Name [_BPF_OBJ_NAME_LEN]byte ///< Null-terminated map name. _ uint32 ///< Map flags. _ uint32 ///< ID of inner map template. _ uint32 ///< Number of pinned paths. } // See https://github.com/microsoft/ebpf-for-windows/blob/95267a53b26c68a94145d1731e2a4c8b546034c3/include/ebpf_structs.h#L396-L410 type BpfProgInfo struct { _ uint32 ///< Program ID. _ uint32 ///< Program type, if a cross-platform type. _ uint32 ///< Number of maps associated with this program. _ uintptr ///< Pointer to caller-allocated array to fill map IDs into. Name [_BPF_OBJ_NAME_LEN]byte ///< Null-terminated map name. _ windows.GUID ///< Program type UUID. _ windows.GUID ///< Attach type UUID. _ uint32 ///< Number of pinned paths. _ uint32 ///< Number of attached links. } golang-github-cilium-ebpf-0.21.0+ds1/internal/elf.go000066400000000000000000000040541520243672000221130ustar00rootroot00000000000000package internal import ( "debug/elf" "fmt" "io" ) type SafeELFFile struct { *elf.File } // NewSafeELFFile reads an ELF safely. // // Any panic during parsing is turned into an error. This is necessary since // there are a bunch of unfixed bugs in debug/elf. // // https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+debug%2Felf+in%3Atitle func NewSafeELFFile(r io.ReaderAt) (safe *SafeELFFile, err error) { defer func() { r := recover() if r == nil { return } safe = nil err = fmt.Errorf("reading ELF file panicked: %s", r) }() file, err := elf.NewFile(r) if err != nil { return nil, err } return &SafeELFFile{file}, nil } // OpenSafeELFFile reads an ELF from a file. // // It works like NewSafeELFFile, with the exception that safe.Close will // close the underlying file. func OpenSafeELFFile(path string) (safe *SafeELFFile, err error) { defer func() { r := recover() if r == nil { return } safe = nil err = fmt.Errorf("reading ELF file panicked: %s", r) }() file, err := elf.Open(path) if err != nil { return nil, err } return &SafeELFFile{file}, nil } // Symbols is the safe version of elf.File.Symbols. func (se *SafeELFFile) Symbols() (syms []elf.Symbol, err error) { defer func() { r := recover() if r == nil { return } syms = nil err = fmt.Errorf("reading ELF symbols panicked: %s", r) }() syms, err = se.File.Symbols() return } // DynamicSymbols is the safe version of elf.File.DynamicSymbols. func (se *SafeELFFile) DynamicSymbols() (syms []elf.Symbol, err error) { defer func() { r := recover() if r == nil { return } syms = nil err = fmt.Errorf("reading ELF dynamic symbols panicked: %s", r) }() syms, err = se.File.DynamicSymbols() return } // SectionsByType returns all sections in the file with the specified section type. func (se *SafeELFFile) SectionsByType(typ elf.SectionType) []*elf.Section { sections := make([]*elf.Section, 0, 1) for _, section := range se.Sections { if section.Type == typ { sections = append(sections, section) } } return sections } golang-github-cilium-ebpf-0.21.0+ds1/internal/endian_be.go000066400000000000000000000004531520243672000232500ustar00rootroot00000000000000//go:build armbe || arm64be || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 package internal import "encoding/binary" // NativeEndian is set to either binary.BigEndian or binary.LittleEndian, // depending on the host's endianness. var NativeEndian = binary.BigEndian golang-github-cilium-ebpf-0.21.0+ds1/internal/endian_le.go000066400000000000000000000005071520243672000232620ustar00rootroot00000000000000//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || ppc64le || riscv64 || wasm package internal import "encoding/binary" // NativeEndian is set to either binary.BigEndian or binary.LittleEndian, // depending on the host's endianness. var NativeEndian = binary.LittleEndian golang-github-cilium-ebpf-0.21.0+ds1/internal/epoll/000077500000000000000000000000001520243672000221265ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/epoll/poller.go000066400000000000000000000144721520243672000237620ustar00rootroot00000000000000//go:build !windows package epoll import ( "errors" "fmt" "math" "os" "runtime" "slices" "sync" "time" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) var ( ErrFlushed = errors.New("data was flushed") errEpollWaitDeadlineExceeded = fmt.Errorf("epoll wait: %w", os.ErrDeadlineExceeded) errEpollWaitClosed = fmt.Errorf("epoll wait: %w", os.ErrClosed) ) // Poller waits for readiness notifications from multiple file descriptors. // // The wait can be interrupted by calling Close. type Poller struct { // mutexes protect the fields declared below them. If you need to // acquire both at once you must lock epollMu before eventMu. epollMu sync.Mutex epollFd int eventMu sync.Mutex closeEvent *eventFd flushEvent *eventFd cleanup runtime.Cleanup } func New() (_ *Poller, err error) { closeFDOnError := func(fd int) { if err != nil { unix.Close(fd) } } closeEventFDOnError := func(e *eventFd) { if err != nil { e.close() } } epollFd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) if err != nil { return nil, fmt.Errorf("create epoll fd: %w", err) } defer closeFDOnError(epollFd) p := &Poller{epollFd: epollFd} p.closeEvent, err = newEventFd() if err != nil { return nil, err } defer closeEventFDOnError(p.closeEvent) p.flushEvent, err = newEventFd() if err != nil { return nil, err } defer closeEventFDOnError(p.flushEvent) if err := p.Add(p.closeEvent.raw, 0); err != nil { return nil, fmt.Errorf("add close eventfd: %w", err) } if err := p.Add(p.flushEvent.raw, 0); err != nil { return nil, fmt.Errorf("add flush eventfd: %w", err) } p.cleanup = runtime.AddCleanup(p, func(raw int) { _ = unix.Close(raw) }, p.epollFd) return p, nil } // Close the poller. // // Interrupts any calls to Wait. Multiple calls to Close are valid, but subsequent // calls will return os.ErrClosed. func (p *Poller) Close() error { p.cleanup.Stop() // Interrupt Wait() via the closeEvent fd if it's currently blocked. if err := p.wakeWaitForClose(); err != nil { return err } // Acquire the lock. This ensures that Wait isn't running. p.epollMu.Lock() defer p.epollMu.Unlock() // Prevent other calls to Close(). p.eventMu.Lock() defer p.eventMu.Unlock() if p.epollFd != -1 { unix.Close(p.epollFd) p.epollFd = -1 } if p.closeEvent != nil { p.closeEvent.close() p.closeEvent = nil } if p.flushEvent != nil { p.flushEvent.close() p.flushEvent = nil } return nil } // Add an fd to the poller. // // id is returned by Wait in the unix.EpollEvent.Pad field any may be zero. It // must not exceed math.MaxInt32. // // Add is blocked by Wait. func (p *Poller) Add(fd int, id int) error { if int64(id) > math.MaxInt32 { return fmt.Errorf("unsupported id: %d", id) } p.epollMu.Lock() defer p.epollMu.Unlock() if p.epollFd == -1 { return fmt.Errorf("epoll add: %w", os.ErrClosed) } // The representation of EpollEvent isn't entirely accurate. // Pad is fully usable, not just padding. Hence we stuff the // id in there, which allows us to identify the event later (e.g., // in case of perf events, which CPU sent it). event := unix.EpollEvent{ Events: unix.EPOLLIN, Fd: int32(fd), Pad: int32(id), } if err := unix.EpollCtl(p.epollFd, unix.EPOLL_CTL_ADD, fd, &event); err != nil { return fmt.Errorf("add fd to epoll: %v", err) } return nil } // Wait for events. // // Returns the number of pending events and any errors. // // - [os.ErrClosed] if interrupted by [Close]. // - [ErrFlushed] if interrupted by [Flush]. // - [os.ErrDeadlineExceeded] if deadline is reached. func (p *Poller) Wait(events []unix.EpollEvent, deadline time.Time) (int, error) { p.epollMu.Lock() defer p.epollMu.Unlock() if p.epollFd == -1 { return 0, errEpollWaitClosed } for { timeout := int(-1) if !deadline.IsZero() { // Ensure deadline is not in the past and not too far into the future. timeout = int(internal.Between(time.Until(deadline).Milliseconds(), 0, math.MaxInt)) } n, err := unix.EpollWait(p.epollFd, events, timeout) if temp, ok := err.(temporaryError); ok && temp.Temporary() { // Retry the syscall if we were interrupted, see https://github.com/golang/go/issues/20400 continue } if err != nil { return 0, err } if n == 0 { return 0, errEpollWaitDeadlineExceeded } for i := 0; i < n; { event := events[i] if int(event.Fd) == p.closeEvent.raw { // Since we don't read p.closeEvent the event is never cleared and // we'll keep getting this wakeup until Close() acquires the // lock and sets p.epollFd = -1. return 0, errEpollWaitClosed } if int(event.Fd) == p.flushEvent.raw { // read event to prevent it from continuing to wake p.flushEvent.read() err = ErrFlushed events = slices.Delete(events, i, i+1) n -= 1 continue } i++ } return n, err } } type temporaryError interface { Temporary() bool } // wakeWaitForClose unblocks Wait if it's epoll_wait. func (p *Poller) wakeWaitForClose() error { p.eventMu.Lock() defer p.eventMu.Unlock() if p.closeEvent == nil { return fmt.Errorf("epoll wake: %w", os.ErrClosed) } return p.closeEvent.add(1) } // Flush unblocks Wait if it's epoll_wait, for purposes of reading pending samples func (p *Poller) Flush() error { p.eventMu.Lock() defer p.eventMu.Unlock() if p.flushEvent == nil { return fmt.Errorf("epoll wake: %w", os.ErrClosed) } return p.flushEvent.add(1) } // eventFd wraps a Linux eventfd. // // An eventfd acts like a counter: writes add to the counter, reads retrieve // the counter and reset it to zero. Reads also block if the counter is zero. // // See man 2 eventfd. type eventFd struct { file *os.File // prefer raw over file.Fd(), since the latter puts the file into blocking // mode. raw int } func newEventFd() (*eventFd, error) { fd, err := unix.Eventfd(0, unix.O_CLOEXEC|unix.O_NONBLOCK) if err != nil { return nil, err } file := os.NewFile(uintptr(fd), "event") return &eventFd{file, fd}, nil } func (efd *eventFd) close() error { return efd.file.Close() } func (efd *eventFd) add(n uint64) error { var buf [8]byte internal.NativeEndian.PutUint64(buf[:], n) _, err := efd.file.Write(buf[:]) return err } func (efd *eventFd) read() (uint64, error) { var buf [8]byte _, err := efd.file.Read(buf[:]) return internal.NativeEndian.Uint64(buf[:]), err } golang-github-cilium-ebpf-0.21.0+ds1/internal/epoll/poller_test.go000066400000000000000000000053541520243672000250200ustar00rootroot00000000000000//go:build !windows package epoll import ( "errors" "math" "os" "testing" "time" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/unix" ) func TestPoller(t *testing.T) { t.Parallel() event, poller := mustNewPoller(t) done := make(chan struct{}, 1) read := func() { defer func() { done <- struct{}{} }() events := make([]unix.EpollEvent, 1) n, err := poller.Wait(events, time.Time{}) if errors.Is(err, os.ErrClosed) { return } if err != nil { t.Error("Error from wait:", err) return } if n != 1 { t.Errorf("Got %d instead of 1 events", n) } if e := events[0]; e.Pad != 42 { t.Errorf("Incorrect value in EpollEvent.Pad: %d != 42", e.Pad) } } if err := event.add(1); err != nil { t.Fatal(err) } go read() select { case <-done: case <-time.After(time.Second): t.Fatal("Timed out") } if _, err := event.read(); err != nil { t.Fatal(err) } go read() select { case <-done: t.Fatal("Wait doesn't block") case <-time.After(time.Second): } if err := poller.Close(); err != nil { t.Fatal("Close returns an error:", err) } select { case <-done: case <-time.After(time.Second): t.Fatal("Close doesn't unblock Wait") } if err := poller.Close(); !errors.Is(err, os.ErrClosed) { t.Fatal("Closing a second time doesn't return ErrClosed:", err) } } func TestPollerDeadline(t *testing.T) { t.Parallel() _, poller := mustNewPoller(t) events := make([]unix.EpollEvent, 1) _, err := poller.Wait(events, time.Now().Add(-time.Second)) if !errors.Is(err, os.ErrDeadlineExceeded) { t.Fatal("Expected os.ErrDeadlineExceeded on deadline in the past, got", err) } done := make(chan struct{}) go func() { defer close(done) _, err := poller.Wait(events, time.Now().Add(math.MaxInt64)) if !errors.Is(err, os.ErrClosed) { t.Error("Expected os.ErrClosed when interrupting deadline, got", err) } }() // Wait for the goroutine to enter the syscall. time.Sleep(500 * time.Microsecond) poller.Close() <-done } func TestPollerFlush(t *testing.T) { t.Parallel() _, poller := mustNewPoller(t) events := make([]unix.EpollEvent, 1) done := make(chan struct{}) go func() { defer close(done) _, err := poller.Wait(events, time.Time{}) qt.Check(t, qt.ErrorIs(err, ErrFlushed)) }() // Wait for the goroutine to enter the syscall. time.Sleep(500 * time.Microsecond) poller.Flush() <-done } func mustNewPoller(t *testing.T) (*eventFd, *Poller) { t.Helper() event, err := newEventFd() if err != nil { t.Fatal(err) } t.Cleanup(func() { event.close() }) poller, err := New() if err != nil { t.Fatal(err) } t.Cleanup(func() { poller.Close() }) if err := poller.Add(event.raw, 42); err != nil { t.Fatal("Can't add fd:", err) } return event, poller } golang-github-cilium-ebpf-0.21.0+ds1/internal/errors.go000066400000000000000000000106571520243672000226670ustar00rootroot00000000000000package internal import ( "bytes" "fmt" "io" "strings" ) // ErrorWithLog wraps err in a VerifierError that includes the parsed verifier // log buffer. // // The default error output is a summary of the full log. The latter can be // accessed via VerifierError.Log or by formatting the error, see Format. func ErrorWithLog(source string, err error, log []byte) *VerifierError { const whitespace = "\t\r\v\n " // Convert verifier log C string by truncating it on the first 0 byte // and trimming trailing whitespace before interpreting as a Go string. if i := bytes.IndexByte(log, 0); i != -1 { log = log[:i] } log = bytes.Trim(log, whitespace) if len(log) == 0 { return &VerifierError{source, err, nil} } logLines := bytes.Split(log, []byte{'\n'}) lines := make([]string, 0, len(logLines)) for _, line := range logLines { // Don't remove leading white space on individual lines. We rely on it // when outputting logs. lines = append(lines, string(bytes.TrimRight(line, whitespace))) } return &VerifierError{source, err, lines} } // VerifierError includes information from the eBPF verifier. // // It summarises the log output, see Format if you want to output the full contents. type VerifierError struct { source string // The error which caused this error. Cause error // The verifier output split into lines. Log []string } func (le *VerifierError) Unwrap() error { return le.Cause } func (le *VerifierError) Error() string { log := le.Log if n := len(log); n > 0 && strings.HasPrefix(log[n-1], "processed ") { // Get rid of "processed 39 insns (limit 1000000) ..." from summary. log = log[:n-1] } var b strings.Builder fmt.Fprintf(&b, "%s: %s", le.source, le.Cause.Error()) n := len(log) if n == 0 { return b.String() } lines := log[n-1:] if n >= 2 && includePreviousLine(log[n-1]) { // Add one more line of context if it aids understanding the error. lines = log[n-2:] } for _, line := range lines { b.WriteString(": ") b.WriteString(strings.TrimSpace(line)) } omitted := len(le.Log) - len(lines) if omitted > 0 { fmt.Fprintf(&b, " (%d line(s) omitted)", omitted) } return b.String() } // includePreviousLine returns true if the given line likely is better // understood with additional context from the preceding line. func includePreviousLine(line string) bool { // We need to find a good trade off between understandable error messages // and too much complexity here. Checking the string prefix is ok, requiring // regular expressions to do it is probably overkill. if strings.HasPrefix(line, "\t") { // [13] STRUCT drm_rect size=16 vlen=4 // \tx1 type_id=2 return true } if len(line) >= 2 && line[0] == 'R' && line[1] >= '0' && line[1] <= '9' { // 0: (95) exit // R0 !read_ok return true } if strings.HasPrefix(line, "invalid bpf_context access") { // 0: (79) r6 = *(u64 *)(r1 +0) // func '__x64_sys_recvfrom' arg0 type FWD is not a struct // invalid bpf_context access off=0 size=8 return true } return false } // Format the error. // // Understood verbs are %s and %v, which are equivalent to calling Error(). %v // allows outputting additional information using the following flags: // // %+v: Output the first lines, or all lines if no width is given. // %-v: Output the last lines, or all lines if no width is given. // // Use width to specify how many lines to output. Use the '-' flag to output // lines from the end of the log instead of the beginning. func (le *VerifierError) Format(f fmt.State, verb rune) { switch verb { case 's': _, _ = io.WriteString(f, le.Error()) case 'v': n, haveWidth := f.Width() if !haveWidth || n > len(le.Log) { n = len(le.Log) } if !f.Flag('+') && !f.Flag('-') { if haveWidth { _, _ = io.WriteString(f, "%!v(BADWIDTH)") return } _, _ = io.WriteString(f, le.Error()) return } if f.Flag('+') && f.Flag('-') { _, _ = io.WriteString(f, "%!v(BADFLAG)") return } fmt.Fprintf(f, "%s: %s:", le.source, le.Cause.Error()) omitted := len(le.Log) - n lines := le.Log[:n] if f.Flag('-') { // Print last instead of first lines. lines = le.Log[len(le.Log)-n:] if omitted > 0 { fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted) } } for _, line := range lines { fmt.Fprintf(f, "\n\t%s", line) } if !f.Flag('-') { if omitted > 0 { fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted) } } default: fmt.Fprintf(f, "%%!%c(BADVERB)", verb) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/errors_test.go000066400000000000000000000053111520243672000237150ustar00rootroot00000000000000package internal import ( "errors" "os" "testing" "github.com/go-quicktest/qt" ) func TestVerifierErrorWhitespace(t *testing.T) { b := []byte("unreachable insn 28") b = append(b, 0xa, // \n 0xd, // \r 0x9, // \t 0x20, // space 0, 0, // trailing NUL bytes ) err := ErrorWithLog("frob", errors.New("test"), b) qt.Assert(t, qt.Equals(err.Error(), "frob: test: unreachable insn 28")) for _, log := range [][]byte{ nil, []byte("\x00"), []byte(" "), } { err = ErrorWithLog("frob", errors.New("test"), log) qt.Assert(t, qt.Equals(err.Error(), "frob: test"), qt.Commentf("empty log %q has incorrect format", log)) } } func TestVerifierErrorWrapping(t *testing.T) { sentinel := errors.New("bad") ve := ErrorWithLog("frob", sentinel, nil) qt.Assert(t, qt.ErrorIs(ve, sentinel), qt.Commentf("should wrap provided error")) ve = ErrorWithLog("frob", sentinel, []byte("foo")) qt.Assert(t, qt.ErrorIs(ve, sentinel), qt.Commentf("should wrap provided error")) qt.Assert(t, qt.StringContains(ve.Error(), "foo"), qt.Commentf("verifier log should appear in error string")) } func TestVerifierErrorSummary(t *testing.T) { // Suppress the last line containing 'processed ... insns'. errno524 := readErrorFromFile(t, "testdata/errno524.log") qt.Assert(t, qt.StringContains(errno524.Error(), "JIT doesn't support bpf-to-bpf calls")) qt.Assert(t, qt.Not(qt.StringContains(errno524.Error(), "processed 39 insns"))) // Include the previous line if the current one starts with a tab. invalidMember := readErrorFromFile(t, "testdata/invalid-member.log") qt.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")) // Only include the last line. issue43 := readErrorFromFile(t, "testdata/issue-43.log") qt.Assert(t, qt.StringContains(issue43.Error(), "[11] FUNC helper_func2 type_id=10 vlen != 0")) qt.Assert(t, qt.Not(qt.StringContains(issue43.Error(), "[10] FUNC_PROTO (anon) return=3 args=(3 arg)"))) // Include instruction that caused invalid register access. invalidR0 := readErrorFromFile(t, "testdata/invalid-R0.log") qt.Assert(t, qt.StringContains(invalidR0.Error(), "0: (95) exit: R0 !read_ok")) // Include symbol that doesn't match context type. invalidCtx := readErrorFromFile(t, "testdata/invalid-ctx-access.log") qt.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")) } func readErrorFromFile(tb testing.TB, file string) *VerifierError { tb.Helper() contents, err := os.ReadFile(file) if err != nil { tb.Fatal("Read file:", err) } return ErrorWithLog("file", errors.New("error"), contents) } golang-github-cilium-ebpf-0.21.0+ds1/internal/feature.go000066400000000000000000000132531520243672000230010ustar00rootroot00000000000000package internal import ( "errors" "fmt" "runtime" "sync" "github.com/cilium/ebpf/internal/platform" ) // ErrNotSupported indicates that a feature is not supported. var ErrNotSupported = errors.New("not supported") // ErrNotSupportedOnOS indicates that a feature is not supported on the current // operating system. var ErrNotSupportedOnOS = fmt.Errorf("%w on %s", ErrNotSupported, runtime.GOOS) // ErrRestrictedKernel is returned when kernel address information is restricted // by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls. var ErrRestrictedKernel = errors.New("restricted by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls") // UnsupportedFeatureError is returned by FeatureTest() functions. type UnsupportedFeatureError struct { // The minimum version required for this feature. // // On Linux this refers to the mainline kernel version, on other platforms // to the version of the runtime. // // Used for the error string, and for sanity checking during testing. MinimumVersion Version // The name of the feature that isn't supported. Name string } func (ufe *UnsupportedFeatureError) Error() string { if ufe.MinimumVersion.Unspecified() { return fmt.Sprintf("%s not supported", ufe.Name) } return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion) } // Is indicates that UnsupportedFeatureError is ErrNotSupported. func (ufe *UnsupportedFeatureError) Is(target error) bool { return target == ErrNotSupported } // FeatureTest caches the result of a [FeatureTestFn]. // // Fields should not be modified after creation. type FeatureTest struct { // The name of the feature being detected. Name string // Version in the form Major.Minor[.Patch]. Version string // The feature test itself. Fn FeatureTestFn mu sync.RWMutex done bool result error } // FeatureTestFn is used to determine whether the kernel supports // a certain feature. // // The return values have the following semantics: // // err == ErrNotSupported: the feature is not available // err == nil: the feature is available // err != nil: the test couldn't be executed type FeatureTestFn func() error // NewFeatureTest is a convenient way to create a single [FeatureTest]. // // versions specifies in which version of a BPF runtime a feature appeared. // The format is "GOOS:Major.Minor[.Patch]". GOOS may be omitted when targeting // Linux. Returns [ErrNotSupportedOnOS] if there is no version specified for the // current OS. func NewFeatureTest(name string, fn FeatureTestFn, versions ...string) func() error { version, err := platform.SelectVersion(versions) if err != nil { return func() error { return err } } if version == "" { return func() error { // We don't return an UnsupportedFeatureError here, since that will // trigger version checks which don't make sense. return fmt.Errorf("%s: %w", name, ErrNotSupportedOnOS) } } ft := &FeatureTest{ Name: name, Version: version, Fn: fn, } return ft.execute } // execute the feature test. // // The result is cached if the test is conclusive. // // See [FeatureTestFn] for the meaning of the returned error. func (ft *FeatureTest) execute() error { ft.mu.RLock() result, done := ft.result, ft.done ft.mu.RUnlock() if done { return result } ft.mu.Lock() defer ft.mu.Unlock() // The test may have been executed by another caller while we were // waiting to acquire ft.mu. if ft.done { return ft.result } err := ft.Fn() if err == nil { ft.done = true return nil } if errors.Is(err, ErrNotSupported) { var v Version if ft.Version != "" { v, err = NewVersion(ft.Version) if err != nil { return fmt.Errorf("feature %s: %w", ft.Name, err) } } ft.done = true ft.result = &UnsupportedFeatureError{ MinimumVersion: v, Name: ft.Name, } return ft.result } // We couldn't execute the feature test to a point // where it could make a determination. // Don't cache the result, just return it. return fmt.Errorf("detect support for %s: %w", ft.Name, err) } // FeatureMatrix groups multiple related feature tests into a map. // // Useful when there is a small number of discrete features which are known // at compile time. // // It must not be modified concurrently with calling [FeatureMatrix.Result]. type FeatureMatrix[K comparable] map[K]*FeatureTest // Result returns the outcome of the feature test for the given key. // // It's safe to call this function concurrently. // // Always returns [ErrNotSupportedOnOS] on Windows. func (fm FeatureMatrix[K]) Result(key K) error { ft, ok := fm[key] if !ok { return fmt.Errorf("no feature probe for %v", key) } if platform.IsWindows { return fmt.Errorf("%s: %w", ft.Name, ErrNotSupportedOnOS) } return ft.execute() } // FeatureCache caches a potentially unlimited number of feature probes. // // Useful when there is a high cardinality for a feature test. type FeatureCache[K comparable] struct { mu sync.RWMutex newTest func(K) *FeatureTest features map[K]*FeatureTest } func NewFeatureCache[K comparable](newTest func(K) *FeatureTest) *FeatureCache[K] { return &FeatureCache[K]{ newTest: newTest, features: make(map[K]*FeatureTest), } } func (fc *FeatureCache[K]) Result(key K) error { if platform.IsWindows { return fmt.Errorf("feature probe for %v: %w", key, ErrNotSupportedOnOS) } // NB: Executing the feature test happens without fc.mu taken. return fc.retrieve(key).execute() } func (fc *FeatureCache[K]) retrieve(key K) *FeatureTest { fc.mu.RLock() ft := fc.features[key] fc.mu.RUnlock() if ft != nil { return ft } fc.mu.Lock() defer fc.mu.Unlock() if ft := fc.features[key]; ft != nil { return ft } ft = fc.newTest(key) fc.features[key] = ft return ft } golang-github-cilium-ebpf-0.21.0+ds1/internal/feature_test.go000066400000000000000000000036111520243672000240350ustar00rootroot00000000000000package internal import ( "errors" "runtime" "strings" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/testutils/testmain" ) func TestMain(m *testing.M) { testmain.Run(m) } func TestFeatureTest(t *testing.T) { var called bool fn := NewFeatureTest("foo", func() error { called = true return nil }, "1.0") if called { t.Error("Function was called too early") } err := fn() if errors.Is(err, ErrNotSupportedOnOS) { qt.Assert(t, qt.IsFalse(called)) return } qt.Assert(t, qt.IsTrue(called), qt.Commentf("function should be invoked")) if err != nil { t.Error("Unexpected negative result:", err) } fn = NewFeatureTest("bar", func() error { return ErrNotSupported }, "2.1.1") err = fn() if err == nil { t.Fatal("Unexpected positive result") } fte, ok := err.(*UnsupportedFeatureError) if !ok { t.Fatal("Result is not a *UnsupportedFeatureError") } if !strings.Contains(fte.Error(), "2.1.1") { t.Error("UnsupportedFeatureError.Error doesn't contain version") } if !errors.Is(err, ErrNotSupported) { t.Error("UnsupportedFeatureError is not ErrNotSupported") } err2 := fn() if err != err2 { t.Error("Didn't cache an error wrapping ErrNotSupported") } fn = NewFeatureTest("bar", func() error { return errors.New("foo") }, "2.1.1") err1, err2 := fn(), fn() if err1 == err2 { t.Error("Cached result of unsuccessful execution") } } func TestFeatureTestNotSupportedOnOS(t *testing.T) { sentinel := errors.New("quux") fn := func() error { return sentinel } qt.Assert(t, qt.IsNotNil(NewFeatureTest("foo", fn)())) qt.Assert(t, qt.ErrorIs(NewFeatureTest("foo", fn, "froz:1.0.0")(), ErrNotSupportedOnOS)) qt.Assert(t, qt.ErrorIs(NewFeatureTest("foo", fn, runtime.GOOS+":1.0")(), sentinel)) if platform.IsLinux { qt.Assert(t, qt.ErrorIs(NewFeatureTest("foo", fn, "1.0")(), sentinel)) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/io.go000066400000000000000000000064271520243672000217620ustar00rootroot00000000000000package internal import ( "bufio" "bytes" "compress/gzip" "errors" "fmt" "io" "os" "path/filepath" "sync" ) // NewBufferedSectionReader wraps an io.ReaderAt in an appropriately-sized // buffered reader. It is a convenience function for reading subsections of // ELF sections while minimizing the amount of read() syscalls made. // // Syscall overhead is non-negligible in continuous integration context // where ELFs might be accessed over virtual filesystems with poor random // access performance. Buffering reads makes sense because (sub)sections // end up being read completely anyway. // // Use instead of the r.Seek() + io.LimitReader() pattern. func NewBufferedSectionReader(ra io.ReaderAt, off, n int64) *bufio.Reader { // Clamp the size of the buffer to one page to avoid slurping large parts // of a file into memory. bufio.NewReader uses a hardcoded default buffer // of 4096. Allow arches with larger pages to allocate more, but don't // allocate a fixed 4k buffer if we only need to read a small segment. buf := n if ps := int64(os.Getpagesize()); n > ps { buf = ps } return bufio.NewReaderSize(io.NewSectionReader(ra, off, n), int(buf)) } // DiscardZeroes makes sure that all written bytes are zero // before discarding them. type DiscardZeroes struct{} func (DiscardZeroes) Write(p []byte) (int, error) { for _, b := range p { if b != 0 { return 0, errors.New("encountered non-zero byte") } } return len(p), nil } // ReadAllCompressed decompresses a gzipped file into memory. func ReadAllCompressed(file string) ([]byte, error) { fh, err := os.Open(file) if err != nil { return nil, err } defer fh.Close() gz, err := gzip.NewReader(fh) if err != nil { return nil, err } defer gz.Close() return io.ReadAll(gz) } // ReadUint64FromFile reads a uint64 from a file. // // format specifies the contents of the file in fmt.Scanf syntax. func ReadUint64FromFile(format string, path ...string) (uint64, error) { filename := filepath.Join(path...) data, err := os.ReadFile(filename) if err != nil { return 0, fmt.Errorf("reading file %q: %w", filename, err) } var value uint64 n, err := fmt.Fscanf(bytes.NewReader(data), format, &value) if err != nil { return 0, fmt.Errorf("parsing file %q: %w", filename, err) } if n != 1 { return 0, fmt.Errorf("parsing file %q: expected 1 item, got %d", filename, n) } return value, nil } type uint64FromFileKey struct { format, path string } var uint64FromFileCache = struct { sync.RWMutex values map[uint64FromFileKey]uint64 }{ values: map[uint64FromFileKey]uint64{}, } // ReadUint64FromFileOnce is like readUint64FromFile but memoizes the result. func ReadUint64FromFileOnce(format string, path ...string) (uint64, error) { filename := filepath.Join(path...) key := uint64FromFileKey{format, filename} uint64FromFileCache.RLock() if value, ok := uint64FromFileCache.values[key]; ok { uint64FromFileCache.RUnlock() return value, nil } uint64FromFileCache.RUnlock() value, err := ReadUint64FromFile(format, filename) if err != nil { return 0, err } uint64FromFileCache.Lock() defer uint64FromFileCache.Unlock() if value, ok := uint64FromFileCache.values[key]; ok { // Someone else got here before us, use what is cached. return value, nil } uint64FromFileCache.values[key] = value return value, nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/io_test.go000066400000000000000000000006011520243672000230050ustar00rootroot00000000000000package internal import ( "bytes" "io" "testing" ) func TestDiscardZero(t *testing.T) { _, err := io.Copy(DiscardZeroes{}, bytes.NewReader([]byte{0, 0, 0})) if err != nil { t.Error("Returned an error even though input was zero:", err) } _, err = io.Copy(DiscardZeroes{}, bytes.NewReader([]byte{1})) if err == nil { t.Error("No error even though input is non-zero") } } golang-github-cilium-ebpf-0.21.0+ds1/internal/kallsyms/000077500000000000000000000000001520243672000226525ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/kallsyms/cache.go000066400000000000000000000004631520243672000242470ustar00rootroot00000000000000package kallsyms import "sync" type cache[K, V comparable] struct { m sync.Map } func (c *cache[K, V]) Load(key K) (value V, _ bool) { v, ok := c.m.Load(key) if !ok { return value, false } value = v.(V) return value, true } func (c *cache[K, V]) Store(key K, value V) { c.m.Store(key, value) } golang-github-cilium-ebpf-0.21.0+ds1/internal/kallsyms/kallsyms.go000066400000000000000000000106511520243672000250430ustar00rootroot00000000000000package kallsyms import ( "bytes" "errors" "fmt" "io" "os" "slices" "strconv" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" ) var errAmbiguousKsym = errors.New("multiple kernel symbols with the same name") var symAddrs cache[string, uint64] // AssignAddresses looks up the addresses of the requested symbols in the kernel // and assigns them to their corresponding values in the symbols map. Results // of all lookups are cached, successful or otherwise. // // Any symbols missing in the kernel are ignored. Returns an error if multiple // addresses were found for a symbol. func AssignAddresses(symbols map[string]uint64) error { if !platform.IsLinux { return fmt.Errorf("read /proc/kallsyms: %w", internal.ErrNotSupportedOnOS) } if len(symbols) == 0 { return nil } // Attempt to fetch symbols from cache. request := make(map[string]uint64) for name := range symbols { if addr, ok := symAddrs.Load(name); ok { symbols[name] = addr continue } // Mark the symbol to be read from /proc/kallsyms. request[name] = 0 } if len(request) == 0 { // All symbols satisfied from cache. return nil } f, err := os.Open("/proc/kallsyms") if err != nil { return err } defer f.Close() if err := assignAddresses(f, request); err != nil { return fmt.Errorf("loading symbol addresses: %w", err) } // Update the cache with the new symbols. Cache all requested symbols even if // they weren't found, to avoid repeated lookups. for name, addr := range request { symAddrs.Store(name, addr) symbols[name] = addr } return nil } // assignAddresses assigns kernel symbol addresses read from f to values // requested by symbols. Always scans the whole input to make sure the user // didn't request an ambiguous symbol. func assignAddresses(f io.Reader, symbols map[string]uint64) error { if len(symbols) == 0 { return nil } r := newReader(f) for r.Line() { s, err, skip := parseSymbol(r, nil) if err != nil { return fmt.Errorf("parsing kallsyms line: %w", err) } if skip { continue } existing, requested := symbols[string(s.name)] if existing != 0 { // Multiple addresses for a symbol have been found. Return a friendly // error to avoid silently attaching to the wrong symbol. libbpf also // rejects referring to ambiguous symbols. return fmt.Errorf("symbol %s(0x%x): duplicate found at address 0x%x: %w", s.name, existing, s.addr, errAmbiguousKsym) } if requested { // Reading a symbol with a zero address is a strong indication that // kptr_restrict is set and the process doesn't have CAP_SYSLOG, or // kptr_restrict is set to 2 (never show addresses). // // When running the kernel with KASLR disabled (like CI kernels running in // microVMs), kallsyms will display many absolute symbols at address 0. // This memory is unlikely to contain anything useful, and production // machines are unlikely to run without KASLR. // // Return a helpful error instead of silently returning zero addresses. if s.addr == 0 { return fmt.Errorf("symbol %s: %w", s.name, internal.ErrRestrictedKernel) } symbols[string(s.name)] = s.addr } } if err := r.Err(); err != nil { return fmt.Errorf("reading kallsyms: %w", err) } return nil } type ksym struct { addr uint64 name []byte mod []byte } // parseSymbol parses a line from /proc/kallsyms into an address, type, name and // module. Skip will be true if the symbol doesn't match any of the given symbol // types. See `man 1 nm` for all available types. // // Only yields symbols whose type is contained in types. An empty value for types // disables this filtering. // // Example line: `ffffffffc1682010 T nf_nat_init\t[nf_nat]` func parseSymbol(r *reader, types []rune) (s ksym, err error, skip bool) { for i := 0; r.Word(); i++ { switch i { // Address of the symbol. case 0: s.addr, err = strconv.ParseUint(r.Text(), 16, 64) if err != nil { return s, fmt.Errorf("parsing address: %w", err), false } // Type of the symbol. Assume the character is ASCII-encoded by converting // it directly to a rune, since it's a fixed field controlled by the kernel. case 1: if len(types) > 0 && !slices.Contains(types, rune(r.Bytes()[0])) { return s, nil, true } // Name of the symbol. case 2: s.name = r.Bytes() // Kernel module the symbol is provided by. case 3: s.mod = bytes.Trim(r.Bytes(), "[]") // Ignore any future fields. default: return } } return } golang-github-cilium-ebpf-0.21.0+ds1/internal/kallsyms/kallsyms_test.go000066400000000000000000000067741520243672000261150ustar00rootroot00000000000000package kallsyms import ( "bytes" "os" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/testutils" ) var syms = []byte(`0000000000000001 t hid_generic_probe [hid_generic] 00000000000000EA t writenote 00000000000000A0 T tcp_connect 00000000000000B0 B empty_zero_page 00000000000000C0 D kimage_vaddr 00000000000000D0 R __start_pci_fixups_early 00000000000000E0 V hv_root_partition 00000000000000F0 W calibrate_delay_is_known A0000000000000AA a nft_counter_seq [nft_counter] A0000000000000BA b bootconfig_found A0000000000000CA d __func__.10 A0000000000000DA r __ksymtab_LZ4_decompress_fast A0000000000000EA t writenote A0000000000000FA T bench_sym [bench_mod] A0000000000000FF t __kstrtab_功能 [mod]`) func TestParseSyms(t *testing.T) { r := newReader(bytes.NewReader(syms)) i := 0 for ; r.Line(); i++ { s, err, skip := parseSymbol(r, nil) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsFalse(skip)) qt.Assert(t, qt.Not(qt.Equals(s.addr, 0))) qt.Assert(t, qt.Not(qt.Equals(s.name, []byte("")))) } qt.Assert(t, qt.IsNil(r.Err())) qt.Assert(t, qt.Equals(i, 15)) } func TestParseProcKallsyms(t *testing.T) { // Read up to 50k symbols from kallsyms to avoid a slow test. r := newReader(mustOpenProcKallsyms(t)) for i := 0; r.Line() && i < 50_000; i++ { s, err, skip := parseSymbol(r, nil) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsFalse(skip)) qt.Assert(t, qt.Not(qt.Equals(s.name, []byte("")))) } qt.Assert(t, qt.IsNil(r.Err())) } func TestAssignAddressesCaching(t *testing.T) { err := AssignAddresses( map[string]uint64{ "bpf_perf_event_output": 0, "foo": 0, }, ) testutils.SkipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) v, ok := symAddrs.Load("bpf_perf_event_output") qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Not(qt.Equals(v, 0))) v, ok = symAddrs.Load("foo") qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(v, 0)) } func TestAssignAddresses(t *testing.T) { b := bytes.NewBuffer(syms) ksyms := map[string]uint64{ "hid_generic_probe": 0, "tcp_connect": 0, "bootconfig_found": 0, } qt.Assert(t, qt.IsNil(assignAddresses(b, ksyms))) qt.Assert(t, qt.Equals(ksyms["hid_generic_probe"], 0x1)) qt.Assert(t, qt.Equals(ksyms["tcp_connect"], 0xA0)) qt.Assert(t, qt.Equals(ksyms["bootconfig_found"], 0xA0000000000000BA)) b = bytes.NewBuffer(syms) ksyms = map[string]uint64{ "hid_generic_probe": 0, "writenote": 0, } qt.Assert(t, qt.ErrorIs(assignAddresses(b, ksyms), errAmbiguousKsym)) } func BenchmarkAssignAddresses(b *testing.B) { b.ReportAllocs() for b.Loop() { b.StopTimer() f := bytes.NewBuffer(syms) want := map[string]uint64{"bench_sym": 0} b.StartTimer() if err := assignAddresses(f, want); err != nil { b.Fatal(err) } } } // Benchmark getting 5 kernel symbols from /proc/kallsyms. func BenchmarkAssignAddressesKallsyms(b *testing.B) { b.ReportAllocs() for b.Loop() { b.StopTimer() f := mustOpenProcKallsyms(b) want := map[string]uint64{ "bpf_trace_vprintk": 0, "bpf_send_signal": 0, "bpf_event_notify": 0, "bpf_trace_printk": 0, "bpf_perf_event_output": 0, } b.StartTimer() if err := assignAddresses(f, want); err != nil { b.Fatal(err) } } } func mustOpenProcKallsyms(tb testing.TB) *os.File { tb.Helper() if !platform.IsLinux { tb.Skip("/proc/kallsyms is a Linux concept") } f, err := os.Open("/proc/kallsyms") qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { f.Close() }) return f } golang-github-cilium-ebpf-0.21.0+ds1/internal/kallsyms/reader.go000066400000000000000000000036701520243672000244510ustar00rootroot00000000000000package kallsyms import ( "bufio" "bytes" "io" ) // reader is a line and word-oriented reader built for reading /proc/kallsyms. // It takes an io.Reader and iterates its contents line by line, then word by // word. // // It's designed to allow partial reading of lines without paying the cost of // allocating objects that will never be accessed, resulting in less work for // the garbage collector. type reader struct { s *bufio.Scanner line []byte word []byte err error } func newReader(r io.Reader) *reader { return &reader{ s: bufio.NewScanner(r), } } // Bytes returns the current word as a byte slice. func (r *reader) Bytes() []byte { return r.word } // Text returns the output of Bytes as a string. func (r *reader) Text() string { return string(r.Bytes()) } // Line advances the reader to the next line in the input. Calling Line resets // the current word, making [reader.Bytes] and [reader.Text] return empty // values. Follow this up with a call to [reader.Word]. // // Like [bufio.Scanner], [reader.Err] needs to be checked after Line returns // false to determine if an error occurred during reading. // // Returns true if Line can be called again. Returns false if all lines in the // input have been read. func (r *reader) Line() bool { for r.s.Scan() { line := r.s.Bytes() if len(line) == 0 { continue } r.line = line r.word = nil return true } if err := r.s.Err(); err != nil { r.err = err } return false } // Word advances the reader to the next word in the current line. // // Returns true if a word is found and Word should be called again. Returns // false when all words on the line have been read. func (r *reader) Word() bool { line := bytes.TrimSpace(r.line) if len(line) == 0 { return false } var found bool r.word, r.line, found = bytes.Cut(line, []byte{' '}) if !found { r.word, r.line, _ = bytes.Cut(line, []byte{'\t'}) } return true } func (r *reader) Err() error { return r.err } golang-github-cilium-ebpf-0.21.0+ds1/internal/kallsyms/reader_test.go000066400000000000000000000015711520243672000255060ustar00rootroot00000000000000package kallsyms import ( "bytes" "testing" "github.com/go-quicktest/qt" ) func TestReader(t *testing.T) { b := []byte(` one two three four λέξη `) r := newReader(bytes.NewReader(b)) qt.Assert(t, qt.IsTrue(r.Line())) qt.Assert(t, qt.IsTrue(r.Word())) qt.Assert(t, qt.Equals(r.Text(), "one")) qt.Assert(t, qt.IsTrue(r.Word())) qt.Assert(t, qt.Equals(r.Text(), "two")) qt.Assert(t, qt.IsTrue(r.Word())) qt.Assert(t, qt.Equals(r.Text(), "three")) qt.Assert(t, qt.IsFalse(r.Word())) qt.Assert(t, qt.IsTrue(r.Line())) qt.Assert(t, qt.IsTrue(r.Word())) qt.Assert(t, qt.Equals(r.Text(), "four")) qt.Assert(t, qt.IsFalse(r.Word())) qt.Assert(t, qt.IsTrue(r.Line())) qt.Assert(t, qt.IsTrue(r.Word())) qt.Assert(t, qt.Equals(r.Text(), "λέξη")) qt.Assert(t, qt.IsFalse(r.Word())) qt.Assert(t, qt.IsFalse(r.Line())) qt.Assert(t, qt.IsNil(r.Err())) } golang-github-cilium-ebpf-0.21.0+ds1/internal/kconfig/000077500000000000000000000000001520243672000224335ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/kconfig/kconfig.go000066400000000000000000000165111520243672000244060ustar00rootroot00000000000000// Package kconfig implements a parser for the format of Linux's .config file. package kconfig import ( "bufio" "bytes" "compress/gzip" "fmt" "io" "math" "strconv" "strings" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" ) // Parse parses the kconfig file for which a reader is given. // All the CONFIG_* which are in filter and which are set set will be // put in the returned map as key with their corresponding value as map value. // If filter is nil, no filtering will occur. // If the kconfig file is not valid, error will be returned. func Parse(source io.ReaderAt, filter map[string]struct{}) (map[string]string, error) { var r io.Reader zr, err := gzip.NewReader(io.NewSectionReader(source, 0, math.MaxInt64)) if err != nil { r = io.NewSectionReader(source, 0, math.MaxInt64) } else { // Source is gzip compressed, transparently decompress. r = zr } ret := make(map[string]string, len(filter)) s := bufio.NewScanner(r) for s.Scan() { line := s.Bytes() err = processKconfigLine(line, ret, filter) if err != nil { return nil, fmt.Errorf("cannot parse line: %w", err) } if filter != nil && len(ret) == len(filter) { break } } if err := s.Err(); err != nil { return nil, fmt.Errorf("cannot parse: %w", err) } if zr != nil { return ret, zr.Close() } return ret, nil } // Golang translation of libbpf bpf_object__process_kconfig_line(): // https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/libbpf.c#L1874 // It does the same checks but does not put the data inside the BPF map. func processKconfigLine(line []byte, m map[string]string, filter map[string]struct{}) error { // Ignore empty lines and "# CONFIG_* is not set". if !bytes.HasPrefix(line, []byte("CONFIG_")) { return nil } key, value, found := bytes.Cut(line, []byte{'='}) if !found { return fmt.Errorf("line %q does not contain separator '='", line) } if len(value) == 0 { return fmt.Errorf("line %q has no value", line) } if filter != nil { // NB: map[string(key)] gets special optimisation help from the compiler // and doesn't allocate. Don't turn this into a variable. _, ok := filter[string(key)] if !ok { return nil } } // This can seem odd, but libbpf only sets the value the first time the key is // met: // https://github.com/torvalds/linux/blob/0d85b27b0cc6/tools/lib/bpf/libbpf.c#L1906-L1908 _, ok := m[string(key)] if !ok { m[string(key)] = string(value) } return nil } // PutValue translates the value given as parameter depending on the BTF // type, the translated value is then written to the byte array. func PutValue(data []byte, typ btf.Type, value string) error { typ = btf.UnderlyingType(typ) switch value { case "y", "n", "m": return putValueTri(data, typ, value) } if strings.HasPrefix(value, `"`) { return putValueString(data, typ, value) } return putValueNumber(data, typ, value) } // Golang translation of libbpf_tristate enum: // https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/bpf_helpers.h#L169 type triState int const ( TriNo triState = 0 TriYes triState = 1 TriModule triState = 2 ) func putValueTri(data []byte, typ btf.Type, value string) error { switch v := typ.(type) { case *btf.Int: if v.Encoding != btf.Bool { return fmt.Errorf("cannot add tri value, expected btf.Bool, got: %v", v.Encoding) } if v.Size != 1 { return fmt.Errorf("cannot add tri value, expected size of 1 byte, got: %d", v.Size) } switch value { case "y": data[0] = 1 case "n": data[0] = 0 default: return fmt.Errorf("cannot use %q for btf.Bool", value) } case *btf.Enum: if v.Name != "libbpf_tristate" { return fmt.Errorf("cannot use enum %q, only libbpf_tristate is supported", v.Name) } if len(data) != 4 { return fmt.Errorf("expected enum value to occupy 4 bytes in datasec, got: %d", len(data)) } var tri triState switch value { case "y": tri = TriYes case "m": tri = TriModule case "n": tri = TriNo default: return fmt.Errorf("value %q is not supported for libbpf_tristate", value) } internal.NativeEndian.PutUint32(data, uint32(tri)) default: return fmt.Errorf("cannot add number value, expected btf.Int or btf.Enum, got: %T", v) } return nil } func putValueString(data []byte, typ btf.Type, value string) error { array, ok := typ.(*btf.Array) if !ok { return fmt.Errorf("cannot add string value, expected btf.Array, got %T", array) } contentType, ok := btf.UnderlyingType(array.Type).(*btf.Int) if !ok { return fmt.Errorf("cannot add string value, expected array of btf.Int, got %T", contentType) } // Any Int, which is not bool, of one byte could be used to store char: // https://github.com/torvalds/linux/blob/1a5304fecee5/tools/lib/bpf/libbpf.c#L3637-L3638 if contentType.Size != 1 && contentType.Encoding != btf.Bool { return fmt.Errorf("cannot add string value, expected array of btf.Int of size 1, got array of btf.Int of size: %v", contentType.Size) } if !strings.HasPrefix(value, `"`) || !strings.HasSuffix(value, `"`) { return fmt.Errorf(`value %q must start and finish with '"'`, value) } str := strings.Trim(value, `"`) // We need to trim string if the bpf array is smaller. if uint32(len(str)) >= array.Nelems { str = str[:array.Nelems] } // Write the string content to .kconfig. copy(data, str) return nil } func putValueNumber(data []byte, typ btf.Type, value string) error { integer, ok := typ.(*btf.Int) if !ok { return fmt.Errorf("cannot add number value, expected *btf.Int, got: %T", integer) } size := integer.Size sizeInBits := size * 8 var n uint64 var err error if integer.Encoding == btf.Signed { parsed, e := strconv.ParseInt(value, 0, int(sizeInBits)) n = uint64(parsed) err = e } else { parsed, e := strconv.ParseUint(value, 0, int(sizeInBits)) n = uint64(parsed) err = e } if err != nil { return fmt.Errorf("cannot parse value: %w", err) } return PutInteger(data, integer, n) } // PutInteger writes n into data. // // integer determines how much is written into data and what the valid values // are. func PutInteger(data []byte, integer *btf.Int, n uint64) error { // This function should match set_kcfg_value_num in libbpf. if integer.Encoding == btf.Bool && n > 1 { return fmt.Errorf("invalid boolean value: %d", n) } if len(data) < int(integer.Size) { return fmt.Errorf("can't fit an integer of size %d into a byte slice of length %d", integer.Size, len(data)) } switch integer.Size { case 1: if integer.Encoding == btf.Signed && (int64(n) > math.MaxInt8 || int64(n) < math.MinInt8) { return fmt.Errorf("can't represent %d as a signed integer of size %d", int64(n), integer.Size) } data[0] = byte(n) case 2: if integer.Encoding == btf.Signed && (int64(n) > math.MaxInt16 || int64(n) < math.MinInt16) { return fmt.Errorf("can't represent %d as a signed integer of size %d", int64(n), integer.Size) } internal.NativeEndian.PutUint16(data, uint16(n)) case 4: if integer.Encoding == btf.Signed && (int64(n) > math.MaxInt32 || int64(n) < math.MinInt32) { return fmt.Errorf("can't represent %d as a signed integer of size %d", int64(n), integer.Size) } internal.NativeEndian.PutUint32(data, uint32(n)) case 8: internal.NativeEndian.PutUint64(data, uint64(n)) default: return fmt.Errorf("size (%d) is not valid, expected: 1, 2, 4 or 8", integer.Size) } return nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/kconfig/kconfig_test.go000066400000000000000000000241431520243672000254450ustar00rootroot00000000000000package kconfig import ( "encoding/binary" "os" "testing" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/go-quicktest/qt" ) func BenchmarkParse(b *testing.B) { f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { b.Fatal(err) } defer f.Close() b.ReportAllocs() for b.Loop() { _, err := Parse(f, nil) if err != nil { b.Fatal(err) } } } func BenchmarkParseFiltered(b *testing.B) { f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { b.Fatal(err) } defer f.Close() b.ReportAllocs() // CONFIG_ARCH_USE_MEMTEST is the last CONFIG_ in the file. // So, we will easily be able to see how many allocated bytes the filtering // permits reducing compared to unfiltered benchmark. filter := map[string]struct{}{"CONFIG_ARCH_USE_MEMTEST": {}} for b.Loop() { _, err := Parse(f, filter) if err != nil { b.Fatal(err) } } } func TestParse(t *testing.T) { t.Parallel() f, err := os.Open("testdata/test.kconfig") if err != nil { t.Fatal("Error reading /testdata/test.kconfig: ", err) } defer f.Close() config, err := Parse(f, nil) if err != nil { t.Fatal("Error parsing kconfig: ", err) } expected := map[string]string{ "CONFIG_TRISTATE": "m", "CONFIG_BOOL": "y", "CONFIG_CHAR": "100", "CONFIG_USHORT": "30000", "CONFIG_INT": "123456", "CONFIG_ULONG": "0xDEADBEEFC0DE", "CONFIG_STR": `"abracad"`, "CONFIG_FOO": `"foo"`, } qt.Assert(t, qt.DeepEquals(config, expected)) } func TestParseFiltered(t *testing.T) { t.Parallel() f, err := os.Open("testdata/test.kconfig") if err != nil { t.Fatal("Error reading /testdata/test.kconfig: ", err) } defer f.Close() filter := map[string]struct{}{"CONFIG_FOO": {}} config, err := Parse(f, filter) if err != nil { t.Fatal("Error parsing gzipped kconfig: ", err) } expected := map[string]string{"CONFIG_FOO": `"foo"`} qt.Assert(t, qt.DeepEquals(config, expected)) } func TestParseGzipped(t *testing.T) { t.Parallel() f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err) } defer f.Close() _, err = Parse(f, nil) if err != nil { t.Fatal("Error parsing gzipped kconfig: ", err) } } func TestParseGzippedFiltered(t *testing.T) { t.Parallel() f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz") if err != nil { t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err) } defer f.Close() filter := map[string]struct{}{"CONFIG_HZ": {}} config, err := Parse(f, filter) if err != nil { t.Fatal("Error parsing gzipped kconfig: ", err) } expected := map[string]string{"CONFIG_HZ": "1000"} qt.Assert(t, qt.DeepEquals(config, expected)) } func TestProcessKconfigBadLine(t *testing.T) { t.Parallel() m := make(map[string]string) err := processKconfigLine([]byte("CONFIG_FOO"), m, nil) qt.Assert(t, qt.IsNotNil(err), qt.Commentf("line has no '='")) err = processKconfigLine([]byte("CONFIG_FOO="), m, nil) qt.Assert(t, qt.IsNotNil(err), qt.Commentf("line has no value")) } func TestPutValue(t *testing.T) { t.Parallel() type testCase struct { typ btf.Type value string expected any comment string } cases := []testCase{ { typ: &btf.Int{ Size: 1, Encoding: btf.Bool, }, value: "n", expected: int8(0), }, { typ: &btf.Int{ Size: 1, Encoding: btf.Bool, }, value: "y", expected: int8(1), }, { typ: &btf.Int{ Size: 1, Encoding: btf.Bool, }, value: "foo", comment: "Bad value", }, { typ: &btf.Int{}, comment: "Encoding is not Bool", }, { typ: &btf.Int{ Encoding: btf.Bool, }, comment: "Size is not 1", }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "y", expected: int32(TriYes), }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "n", expected: int32(TriNo), }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "m", expected: int32(TriModule), }, { typ: &btf.Enum{ Name: "libbpf_tristate", }, value: "foo", comment: "Bad value", }, { typ: &btf.Enum{ Name: "error", }, comment: "Enum name is wrong", }, { typ: &btf.Array{}, value: "y", comment: "Type is not btf.Int", }, { typ: &btf.Int{ Size: 1, }, value: "255", expected: uint8(255), }, { typ: &btf.Int{ Size: 2, }, value: "0xcafe", expected: uint16(0xcafe), }, { typ: &btf.Int{ Size: 2, }, value: "0755", expected: uint16(0755), }, { typ: &btf.Int{ Size: 4, Encoding: btf.Signed, }, value: "-2147483648", expected: int32(-2147483648), }, { typ: &btf.Int{ Size: 4, Encoding: btf.Signed, }, value: "+2147483647", expected: int32(+2147483647), }, { typ: &btf.Int{ Size: 4, }, value: "0xcafec0de", expected: uint32(0xcafec0de), }, { typ: &btf.Int{ Size: 8, Encoding: btf.Signed, }, value: "+1000000000000", expected: int64(1000000000000), }, { typ: &btf.Int{ Size: 8, }, value: "1000000000000", expected: uint64(1000000000000), }, { typ: &btf.Int{ Size: 1, }, value: "foo", comment: "Value is not an int", }, { typ: &btf.Array{}, value: "1", comment: "Type is not btf.Int", }, { typ: &btf.Int{ Size: 16, }, value: "1", comment: "Size is wrong", }, { typ: &btf.Typedef{ Type: &btf.Int{ Size: 1, }, }, value: "1", expected: uint8(1), }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Char, }, Nelems: 6, }, value: `"foobar"`, expected: []byte("foobar"), }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Unsigned, }, Nelems: 3, }, value: `"foobar"`, expected: []byte("foo"), }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Signed, }, Nelems: 2, }, value: `"42"`, expected: []byte("42"), }, { typ: &btf.Int{}, value: `"foo"`, comment: "Type is not btf.Array", }, { typ: &btf.Array{}, value: `"foo"`, comment: "Type is not btf.Array of btf.Int", }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Bool, }, }, comment: "Type is not btf.Array of btf.Int of size 1 which is not btf.Bool", }, { typ: &btf.Array{ Type: &btf.Int{ Size: 4, Encoding: btf.Char, }, }, value: `"foo"`, comment: "Type is not btf.Array of btf.Char of size 1", }, { typ: &btf.Array{ Type: &btf.Int{ Size: 1, Encoding: btf.Char, }, }, value: `"foo`, comment: `Value does not start and end with '"'`, }, } for _, c := range cases { if len(c.comment) > 0 { err := PutValue(make([]byte, 0), c.typ, c.value) qt.Assert(t, qt.IsNotNil(err), qt.Commentf(c.comment)) continue } expected, err := binary.Append(nil, internal.NativeEndian, c.expected) qt.Assert(t, qt.IsNil(err)) data := make([]byte, len(expected)) err = PutValue(data, c.typ, c.value) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(data, expected)) } } func TestPutInteger(t *testing.T) { t.Parallel() type testCase struct { expected []byte integer *btf.Int n uint64 err bool comment string } cases := []testCase{ { integer: &btf.Int{Size: 1, Encoding: btf.Unsigned}, n: 0x01, expected: []byte{0x01}, }, { integer: &btf.Int{Size: 2, Encoding: btf.Unsigned}, n: 0x902a, expected: []byte{0x2a, 0x90}, }, { integer: &btf.Int{Size: 4, Encoding: btf.Unsigned}, n: 0x01234567, expected: []byte{0x67, 0x45, 0x23, 0x01}, }, { integer: &btf.Int{Size: 1, Encoding: btf.Signed}, n: 0x80, err: true, comment: "outside of range int8 -128 ~ 127", }, { integer: &btf.Int{Size: 2, Encoding: btf.Signed}, n: 0xabcdabcd, err: true, comment: "outside of range int16 -32768 ~ 32767", }, { integer: &btf.Int{Size: 4, Encoding: btf.Signed}, n: 0x1234567890, err: true, comment: "outside of range int32 -2147483648 ~ 2147483647", }, { integer: &btf.Int{Size: 2, Encoding: btf.Signed}, n: 0xffffffffffffffff, expected: []byte{0xff, 0xff, 0x00, 0x00}, comment: "n means -1", }, { integer: &btf.Int{Size: 2, Encoding: btf.Signed}, n: 0xffffffffffffffff - 0x8000, err: true, comment: "n means -32768(-MinInt16) - 1 in signed value", }, { integer: &btf.Int{Size: 2, Encoding: btf.Signed}, n: 0x7fff, expected: []byte{0xff, 0x7f}, comment: "maximum value of int16", }, { integer: &btf.Int{Size: 2, Encoding: btf.Unsigned}, n: 0xffff, expected: []byte{0xff, 0xff}, }, { integer: &btf.Int{Size: 4, Encoding: btf.Unsigned}, n: 0xffffffff, expected: []byte{0xff, 0xff, 0xff, 0xff}, }, { integer: &btf.Int{Size: 4, Encoding: btf.Signed}, n: 0x80000000, err: true, comment: "outside of range int32 ~2147483648 ~ 2147483647", }, { integer: &btf.Int{Size: 4, Encoding: btf.Signed}, n: 0xffffffffffffffff - 0x80000000, err: true, comment: "outside of range int32 ~2147483648 ~ 2147483647", }, { integer: &btf.Int{Size: 8, Encoding: btf.Unsigned}, n: 0xffffffffffffffff, expected: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, } for _, c := range cases { data := make([]byte, len(c.expected)) err := PutInteger(data, c.integer, c.n) if c.err { qt.Assert(t, qt.IsNotNil(err)) continue } qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(data, c.expected), qt.Commentf(c.comment)) } } func TestPutIntegerError(t *testing.T) { qt.Assert(t, qt.IsNotNil(PutInteger(nil, &btf.Int{Size: 2}, 0)), qt.Commentf("slice too small for int")) qt.Assert(t, qt.IsNotNil(PutInteger(nil, &btf.Int{Encoding: btf.Bool}, 2)), qt.Commentf("n too big for bool")) } golang-github-cilium-ebpf-0.21.0+ds1/internal/kconfig/testdata/000077500000000000000000000000001520243672000242445ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/kconfig/testdata/config-6.2.15-300.fc38.x86_64.gz000066400000000000000000001673521520243672000306170ustar00rootroot00000000000000%wodconfig-6.2.15-300.fc38.x86_64^=wUՊfL}?~|>gWgN>l>?]?|>oUp tvp˯YT塚7Zѽ:z[6667({^*7z3W)Fzih d'\UIM:3ݔ4bvjb+a2DֺkީSxhk`:C5\笢:o4QJL$-)9-=4`C_p,%X)VSH$3~~~zk:ch椫3<\rRw?'#rZ7UJ.kd{]юơ.JKHR״{^K"A:M*gj,˿~>=+[І 2%q&d/4U '纶B"kiHx'5+HRQ4k.$"B *a M2^`dTՒ4C naD `j88Qu3s&vȖIIѤ+r_÷'ӭi4Έ\s~H2T:=U}~xt@6Jԉ$K+cd50ɾtQKݵ82٫FИn;ԃFOݍ$z'b46[nO vVtV Ay6ed=_=?^oOcB@R3 *G({|YoDDC;; e"3T)ڪeޝ:$(Ttdt[ɼC-9d$YT'o;l%LsZ"V2"5`D᧦{1^;jJ5uQ4&` !i&s4 :ÁΣ*+ryS#Dm&'Rq@<,W.L'6iGHÚ;5oG2pvz~y<>YЋӴ"uk+-֞]ۂO {W4w&%6uMM`*yr*7'=0ۊ1r8[!yW_u{s}/<Ήl6y :^ X>2$mpF.` tc2?1 !,{ z|2‰o4O)Zmi MHa ;MМ044˔pDޏ2Etm^vr- @}cYݱ@aT3g4LĄ5㑆('Q@):^C.1X] gn[mWe- 8e*X(+p l P_ g`<3:Л5Ȃh@ ؔGI8GS v5L35YS%E5qS@&HoUhKҀ\9v:߫{7ŀa 1 X )5Bw4]ŢSheS6 4sIJ jj 2zݰ#mi@>[ЎHy\un~=toV4rf. \,=U@`vَI:Jڒ;EC\; Q [nMl%J zi& Nh֮ >;Ee^o=te,>=<]_W?{pX)Ov8)#,^jmsDn(ZY&b<.1ڨ:~d >{$2]3Xo5P`Xd󂟆jREJ٠SAQl:7Ƞ,*\6 <QyBo/u"X"0*zi0**YRљ-1rCcWQi )!h+hn˃{PQSHPg01>>\W?a!X]+DuZ{GOHz[M"r&ˈ0@c ts5htuҒyvw]HG`OfB_2RO\׉`8:S60Snu@~( ks+/5:Ȗ5&Nk]?R:{7?tڜIH0e^rWG@@UuEfk)LűۼQan{|i<`H1DklUrTfli y0^0f1vXo?GoXxFk)0ȔF}Zk FSk$fd rۧeAW.apUm$'Fj1/c=9X )~seI-%5Pe' gt`wDemhAhlyc;#e1⛝Z:CЛɚR=W] A~a>@WV a g{31.K^en;/8F/C8KwkS2 =/~. +tes6s@l ȐYh+u!Oꄰ: a.,A_,X"8'`H0,.-!'HTZWQq1 qBpclқ$mـxQYGɔcΓD='w{v.bjiLy mJ'MSkM:zxu箿X13ktb\K.1"!Y(,MXA :AL1/0u@ƞ[/ 30$xi(C0.HFS1[ erob3Q4oC#BѧqU 9~5qP=ǵ|tԓVɖ$!@_ju6ut::[y9F}yU2k_ iXq qXdۉa+Gb1H.Yy?s;uD:\S˶0Hn0?6ڗ(_ 4CEܭƳ˘idNpj1B6LiۤM/ 0-GkS;q}CN?;@h@j"H}]a]Fg)Sj|AW 8ZJ :ٯAʛ/].?Ԭ(Un@ԛ]qRMJ$|,J*cp8 Jf[nPK 拻FHdrJW0ǜBvf*8(,wjsѩ`6 y-:B³/hMEMװ{z+}Zo`3|4#IYKc݊ZQ1m1}F@_bZJ3 m.Pf)~c0dlӟWǯs;J'¾;K8jbjt](j?Ͽ!EF5emgվ{y@~wRj`L[ ZkWqaI0_Y;J/ld"I'lI_=\ѻ۫9LMxy$IX@l}5+I0Wapʛlfddf?ʤƭub(o!u3*B4+E`]yjS:{i%@<[~<cqޘ6S>ix~&Ԉo{x?|*ZtXܫ@2 ܹCD]%lNTy؜ _HeDEڜx YnX;tBUK:I/\=^y|Ɛǿ$iaʢM٪K=Ad𾼪y6$(!m،ɡEkCB}Ȝ* + P5+ϦLjS酏\RGHNq7'2p*|csGW]$6 lMll) 0L7B}&Ϟn 37 gR4gaR/v_SS2wZޤRo@7.}C5ǻeQp-h=S#ȭX e/dg/snh NE\Y_al())sd Ϻz$u+ qJ~ИI~ݵ.z 50Ƞ1/8 D&޴cF"> 4RJe:UQ j 22C!)TfDg!Ȓ4zL s][ݮ28l,JJ~k\]d"ie A p25I)xWDvb:]xDLiS9(lN1kAU`eJIj J}hPtln-8Ifr5in28e Q= KqJH茣<+qM9.]cm_!M*嚉%̋No]*>ijJR^ y31I{el5r>YpJ!dry8]nߟ|x2n?>DQ`҈lQn4`^( (GA^#,KUOD-CxWگ#BYn&{E:hYwn쾥"-9 ⶚+z6{d꠸v4tRĊ|/y-CuisaM}MFo^ <,1y=ɿ u'VK_˷?~b'SLU(GI] G V2xmӉzIf4;i#(,[w'6 67M]w-2Z+x$;UrHja]đ4x aBU}$YG`?^H!cb7:AzJWBGX&I-TM9P+4Y51!`9YHyC%"<)DBoN^J:9bR0!Ηm\1g 0Gx$01g0V0MY DST-D򺜙^w(:ۥ6֕ CPh.4e0B v7{N鐰AEbdJYۙiʵAMTDK -cV5):%3>p$,uʂ i$fL3ݢ)#Auʂx &ܵ̎y$0fTb kk "&Q0@bd@G$EL_"ko[U%Dt*s!_c  )[deEO>&bGAtHQױ$c?I/H6Ϛ[kXLC({!IPQW`/OG֚[{:nq또[{FBU V b0RIrkZ$Yk#LQ}SHtoD 5T ۫^U9q緡TofŌRc]9*,b@6^k{=R* Q_E.` 08཭nA ȞE&OORyE'ECiI1A*5,g9`}}?__},9Sǃ:),v`7y8HP{*6ʻs6Z2eܲnW%r3 R ґQOɜzc${ TbxR\D; "L69jTUX>4_$BdY:7Y[;($,t1(;xY*TκB|V[mn/Lc~ (Cvfv-`fk@(B.k UWz |*P^iCv-Y p]Y"&~g9M4\eh `tFda0sPՆfEeTPR ɛ WB-=tMRepjFs%itthf6)tR篠MV|I2OHcoϯjd.G#(.s-Bwe;UOx\/s;(l nhiifX ^%D̼ev e 5ʔ &n56Ӹi7[a00A7 ]q*8S-υuOR@?/0Qt>^d,T4RZ)K:2ykSLKz/t)/2])4.|9=M=.,ef\GK˥iylO,wywƘ~6K\e:O,|NХ4rzX"xݵm]bIü[{azaXp4,?%-, -1\y\Jxnϣl 4b s' 8g@p<ô`D-]hLE,DX( ?TpN4`Am$!}%討RPX`ʘgi1w8ݘ+S#dzWMx31VEanl 0A_\:1W%ꖜ[sw5y٣ ۮ4X$pN? @ɭ;G2p8p.;+[bl]hK,#ۻQ-EKȹ0s=2|3>̡=^/mJZ""U_}@R@ HSSÙ ޒqQiG#Xgo[kf4i?aP&^=WjZKي~XHE}ffI'^\:\TKQMjJ@l]n(:QݠBD$P~f}*l ź Yp'q2@\-!0T>+e5v6(T2{p ~6ZzJRBM#Z {; !U*ÊJ 1`qV`;@ĎrTD Cx)jbTbpS/88&ԋIR=:)Eƙ UB#;*Ҵwɛwׇ|v1&*࿅D!uܸ]wf 3"9ZO\YN_}UbIR?[ aHOb&R5ۼպ0$٧) rCzjTRTˑLS1$Nq)%*۹q0Jo1tٙkƳgWFXdaZU薨X'l46< O3W,=pyW%a/<_ ŦUKQÍ HB,!)i렴vH3A&NsR6 âΉh곩ʘCfMUFU:6/EFu2[JHQZiJFհbKJ6^VP"[YUǁ`oՃh/o-X$I {x_؃C鏔^uA3Q) 09eL`E<_#iY9.44DJ kgo:ڡAҤezՒ]G[s 1B&8] ZČO߼O'E\фif^36$%bIkaL]ʗ鋞x@dbЍ*ND5c6z'$' \>vP˲/0ؤYjHLMe-ݏ"j:e0_CQORi|k)c 2@F:}4,T&l 8:jq+nx22Ǐ7Lф=4>~Y4P;H J۔p0 /Ce?/vts[9Տ)W'1c{p%\|{ iӈї6y7r ߼Cpz_5(} DbRP"o"m8VԄ% 8|kmב*`:V6sF ={4į<&[E}+cX\Tq_umvN\OU5Cgt)NNnb+ǂi2s8+C(4(4d:G#O8TZS@B`1h@~6WiCBҼ7Jo3ǑI/Mq61HcD0`{@p4]DMڱ{' 6%Y4vavbu3?}dYo lO:5l#m ɜN\ן>SOca&K<9>k~5mfHBMڨ)lrfm{YY_Z =š6 )ׂ5ÝyؠiՐQU`Ny<& s!ϑ~Bi#[P861X;@Dl `U ?%y<؞_/><_?˨?F7dM;V!ζHE:3]Ev_?<dc+æBW'le OpYz'۝=:걊š%XIE`RCe/Yo.h P ʛ5r? }!'83=4R Jq̔fQOх]`l݊6ɏ"֍QF0)JҹcD.j%Q" /!Zb0dg\)9Bypij.&i1$968Ba *(UM4q@P@2)#'>l'*ft#,&_/9D ѺXPP[L=&nb%䳋JH^^OaS'TH ?:#878aWo 谥Jd ZhJqA!VGǬxqkꍼ)oo/tgQX}y{y3-+._ֳy?Q% .).VhॻKD17w\4[.#,źX0XvJV C9TS`XU<:TE#˗o!xO񟚤6NyN]FV.Wꈈ,CYQM_LچEV_tވЕeCPi+)ۦsa!Fbw;5D޽L$mGE1\fKWN3Z2X)B{/=I;}nvWq5DEpmئ=6`Mydq(rAqj_x}~ǘ:K苎r,uMLۗ`ўigMFS.Q E~y8D?_" )z}ˇ_^ CAW6w9;IięYB_™j]UAC'q0/&Rô _%Mv(c΂~K4vZj_1UTgexX妅O7) "w+g n%uu)_!46NTO.seK,sV( 0C~rrٺƑ`Xze.=xAՋl&,r%~mՆz_[*W|{kB?~՗c~1w`{Tj ƀ(u?Ţ4GF4El ϟ>|~{O-raBv;W8Wy8Jq,bD^vp3Ov]H.8ޣG5ڗ jK)H_(j//y?4ՁnjŠ.N ;KHa %荹814wh'_.>W5lpwL~m3qU?)7x~OF&yY Q֫¬Fk`TWV&=i"4\w-}1(D#@:̯:M R`F䪕ox&h_!:u|%4"|NDLPtYGS(8aB=Q^Mk~sO|c w>Ee+j\u`¨Ÿ$DK1jJ$aztgTP)NͅLb(BTnuqwnv33CGY'~Y.>(ru&I]Ѫɥ(Qb}ȑ[² 3YV#KBCW/bːYi]w{W~l.Yo6a~出;X鮒⯴.oe!\xV5NoOm^4(G0*'汨mE (>!'sҶLkX%y1%>CDԦbD,pct.Vta@5 pvnc D![w1,iTLSoZ6FC/y+(P׈֨bKQÖ ?{``27}{'8} ǿ>|/zaECרQ^)CZ;، uG3|Jk}<=1IIz6cC[9S\|AʜA.PL?QX}Hi^^;]#{RЗu 5:}K]GƜZ,/ dV6Y"_M̠,HS"ND{6Yájozy1ޞzq򳘸UNa7і#u7iuhn2xD.$*wvߌ$6ϒ$) YC:eҞ'EjꩄhEl M$)H>jc(&&)QNB'E7Xǁ\'玑ѩbJHp:x?vׇ،WaaLx2xDŽr0{ɠ4mV+lRGdh2:&oENdk`!D:5 |. ,2DgҰTKtʕࢰ5W+2 xR7?SuxAx{u2XQU~?g^b K]5);xҕ)%׸YPm%C&#JCeEjoSsO&b}Psx{_=`݆Gݶ1.9k2:Z=p22zCtk [`(J{c_Qq qfaqp\8sߒw0Džr4vدVdC%h+aUd6?YӍsL7Ih/S ВvU.J@1)̳zy8i,[IH:N,(dzg"-ֹKiR ZX ,%EͦRnfS ϕ<–d%G:FBW@i.i묏)z)Qƈ. &r%7B iŦ0?z9B B4.(f~ Tu_|~v*s/He6 )n */)4*`6`#7 & IPi(+ u h'r04]ʷz &E~x$vuP|U`lPX%ښj aYMB3D"7VFլ@U_>}7g15]aYR}NZpB1WN[<> ikB+#a"9Qz =uHSYO"+ lfZo_hjj5~D1)~T'S6ZWhX&tX`7!+nKGe&6Fş]8_jol|:`z#~'6`zeb[!'#6بi>xaח?ǩ5)wB 0\>B(*9L01vq'_?jKԳ ط#8x '4xQV0˔"N 6էT)a֭~87nTⰯ;hy0gckq,:|T=Vҗ>ҙYBh%az>qIj,G"k|ot>۳DX1- 4fb(C/+୴Y.2Z;zr(`&MT5P M-,23&5 Sx; FZKl &5fNymӒ^._/zqb\-rޥy軦)vݫ4R5SRB2o1tpv&\JXD8֡ Ôhc8HA E&$@4Kŷ^O!{-D F[_]Ɗ0ӬVzmn[i%9f.fNCE4l%!t ^HdƸՉV:ǡbYig7| "rbܞ;g OyBSb2}DtgJ- Pt7q$?ެcxx"t'cǨ(LDI]KV}pXY\ag +KZˬSE: JOt>lGbI *x a9tk<(Z/>؃SWP?<׶2DW 2aLJ8B"HaGc‹2<,&ѝaoWkzҍ6^pm.w,%1nȶ>K/-M͈ՀD;}˵qM(x#4}\.3K*ȯeJHU.<ԯ9j]{ (FAՄ!0dR}7 f`#e"ZckE|b=|DZL!՚%WTs[ Ң|00QO@ `6ZG+w|GLi$v"<-]`W>Um^\mzqE j A==A?Sr}D\,xfzoN~t ;3!Tl8{^ACyBl72`kN6Ot eN4gI^vL y.FBƎ`'ZJo'y/h+BhveDX&RjVN{4g,wxC9W1ANKgd"_^Y4w7j#znJ,])E hրm,scyLWKw FlŘ0lѠ8ф&S 82DoDy1b|к>tF6 &џ4!g-Ԗگc<ڕD/H{ qsJTn@bpJI ml0m\}.Tvh{ JZH6*z QI~sThЈvaSs쮩eRa#LyK h@zVi!!/eY8UPtx[9ۋK(L/II@Vؼzfkn9_y/PZ36UL8(;cѓRj4AHwbe]8@fA'K!XD5]x^zxybz֒p+֫oN?y C‹8 mT~650ZSN@v{30 nʥd+4Z׆5\g40V3ki̎Q0(lcGf^{@ :cOҞ.eBzߟ>Z:N!Tݬ@܎-0m9[[]Ob&i&VZhumpI2#,v]Wa[ %kqU AҐO\)LN1Mp~H/c-J4BZـN!cʷt} n,(G7c5 :{}hS$$O[ PAO@("FP8o;|~({91tD;-r̷)MOdEwoʄ)xtKs ^iأlҰw cQukcbB_:5Jc.4Pu~ gkr#:QLn7]ʉW68[ 1;>Yq6M[oWħOha@l''y%̙=hP:3R=Y-咱O>3#ڽ9է@wqw852Wy# 8=G*m(mdTcX4D8fg&^J'h{fN:8UӁtV#gn9j8BWI(%52, %..< ޸Fgc&x] ]2l 7!F,^]ĖB_&'Tfh z3>}' 89xMF3\HZ>71TY"Z -SU4\,O&7xIpFls 4P+Π!uڱ-?i[Ѐ*K٭O6λ9YHoܪ6&߮F]AQoX Xo0⤡Pӿ(mZݍΈ:LhLrz&8'01~F'ω \9u XzC !h޷|O{bs]y="nkj1,|l/8vb6(v QtB3rZ!s66P(p#N%@w3˃Q9 &:cT V11q2K,cwcq"BMw^СM@ z&N :TLٸ(w_SCiw@jrTolܔ]@S66.幬nI3uƀ;./3ftD#/7Md6@Hյl\=,Q7; 7n7@܆AhCpOLh{_%0?KvXBpub#b胒Rִ^?RaF23@ }-P4(!83% Y_$z!+bIs >QR`D1<%ӓ(Wl8F;~Xh;Znt?J* g%)X`Q~+\O4`ݫ_ yHV.Yݷu'귙Va$N{~[̵zhۀM]"C57}p4R6fԖVg(؄O"JܤP1a? );Q3AsG$)l&fZ5McySl}:p?k 0jǍz2_R^}{=_/{y k0ispxET;=o=b BO?tLLQjb1Ɠv寿ߜa"YY_Q4$ev=O>%as\JJ (ʕ]Ex L)fe~>Ե6ڳ}[oN!-?+' 2g~5[j(%X:Mmnvp_{wz^͍;( {eu`sg22OZSi ˝jV*e%3ٙn-|u)'U΢kF&zP;V3"i7S,t>a^s  #yw]SDT5,{^ ehY.Ҩ ^RF@ř9]h*^RZJQJ?3SVpBZ>]Irɕ1)`)8~ 7 kKww]"O\*ުH!*38;2cWs4+MbeWimn]}AR.L/E4$1Pq'mA6x.sY'-lVQu(£t E¯TM"Ebk K @}H߀KU&̐pV^m |ŵs=s)'$U"ܯ Ŋݶ)\^GBvVW"N į1] os!4Dmm YI}tNx NR$٢voskOie)8.ܘ-_ݩ]:!^X9_+ugṉ /T-u& w~4LEn.eˊt'`D΀4jbTFS/G78eݕܰTDq0Fw4-3  P/~p9atn8q>/Ґ'euX%pgi ]<+87g".Րq2hX1n ~W #u*kC.``5 &n`Y6TlE*'XiwMB ъ>ɥ0mX&\Qu2dC:#epq&Uώ^fJr݇suL]"\&tuL-e運`>g Q/e׉J`G>u#4KڞuTVd{V [\P /On)=8H!J6kZ2We~-[VFg?"LW0Q+*2-*K djU2 ɹLt!)B6SGjU Q&rWϗ禪؁VҫRxOHZaCB)5:(~qImƚtPf9:75Ty-tP ϳS"[HXS j(04Hb`m,ÒqcP 5fW3I(I`^N-/ת5%7*@de;_Rp[OHdf" wv?™g1㼊YuQ <'8G٭=إzm+5g,|J(.:0<&  tSwWǿ(awHRFy:kY*Θߋ-X)ߵ|>7Ieb\fspwo ȓ0$ :cf>y,%yb Y O^>ˇ_őiN9K75sؼ]EANr>Mrw5+"vtE:e@8Mcs([l _ALVf*HOqo=c|aaUKu MUmvy6ck觖O lpZjt^F)@WR@R|V|‹>W{hb .Gԗ4p p)Y'ީ8qEVPKZz5HjӘ)hn io0y/PQp c̈́$C1Iߌ_Mp̟ౌ%̫qÌE x \sb^ټv})Dݧ?)2wy'㱳FGЧ1P;*ۋx9XƮOWtΌ։*ǢqDb~k YAVvၿ~O ?kU`\-0g<ݩ S ׎'}uKW+%L/_޾QH,-,tY#|C1Lk+hT?^>}}EۀyBNË4mjsq:LsufI XbɯmzU\;aS^ k@;:,s+YRݤ(l5@o8l:tK6 TPW;osO}xSRTS4OWlZ$6i$SͶI9SDMաaYV%(N&BnR9adiQGhӎ)ӱ8 v5Ќ:sTH۴r/ex,-;NUT1"O#Vo߈4vL3F,lvLH5D`ș_7s{,;kMwj92X005]@lӵ篯>?8&ryZ NHHW:rtx)5W3>Yih;ZP~q/ِx#nڝ@5pۓT{jO"es3XUemRӦ/&Kwv :_ܡM1`/ڪ ,v~4ڬI}ң6&[:zjq[ՌBCv֣j{b>LAmueepk=P߄i]Xڝāo጑8 \R#yq Cf⍛5`к54H4mHEP%OؤG>T KZ̐zC ȚNt'wυ8\hͧÚ7H7, ׇ*HNnxq^S,n!ڧz/]1Sd8\ts?B݈Fux}M"#,ŪZ<Hw4X8 bi֞1#Yܧ}0@&yI'X1@ϖR\+S+yoNB j4j3aF>9UldtgFp@a.NVPz|P#t b:X,BGU_ːG]n$֏|I_/:<9]#UjK%rLnE8,3Fx ,Uȓ[dqnTs9SRc)m}x~nS@vHNi3=:@qX.,U a+#1M" ;jAKñʓ¨lJ}Vݛ,u|wgص:>Jfm/&aLq9.ۉSȟNo7L:)k}r4] -ޚN|Oᅷ7.E88gۑj׽τvyB"AΕnCz>^cI}bL̆lxTc:gO*$6սyM YXx_"&(6~v֞@T@XD6V!-q+a@ved*ʧ Ee;jȕﺶ?79-}UݑE}7-)L9o'~G w[Sy_goڂԆBM7$W}DnQYVIj~WaD/y}izVe>06w}xUv!#NUUۧ%, QDF>Dnj3i0$o:$o>?ח_p*:WCIot &;CWC^e-H疄-x?Mc hYQL.Lޙ9Y'e:ݝ5)T'!:axhHYlt<ũ8~(TqFmb mUR9gc_sZhvMY/xcfi`V)g;v-,3.dSo*+oaN,FB:`xY8*[Αa8 _rj[9cvin/%zy-Y˒,ս\[ocȕ#bZ\eD9sdK06Ėi3 `_g_ &GZf0nW Yi'}t!'Lu28d0ywͽ^r,`ϵw^x ǝ#L]~n6'ʚ6_an5bHfb$fE]c; @CVYK F)~-~TNgoT2̇&Wz4%tq]k83_m $IjvU궼X%LivN̠5L~PQ0 31=pr*;[?zLbɎf.7m`r&@7my!4XXԿE}Sط*ZiFYlefK;I>)%}q\i]e]%o)U ]ZЂ%H`T0Bm3uu} |ˉy@Q)2[Le6zO38F- @F@B(}=|ï+,mr>uH+sBd>&mr3(Կ M6*h2UyoN~"%]T5t6ӴZf4=lC÷mS}ӷ,O*d{Ы,mJ}_8/[x 8ya 0-?~NQ*Vr7-~zPhZ_DW}rw \jߒ:ZemOk֚ƺQZOE>NH'%D[[-G)jSPӢ+]͘:Bkt<ǘ\"Zڐc Or:0x|w qw~Ik=Hn8zΖ.RCyV}p d=H49r!"% vdcg*#u!9jr0 H I 74ȄL]p!W3f0u/B^hAD& 傕3DT3CR6V> ,Q}rT`B;eOFif-.O-pDs.uE|-67I9u#$lz]UnA%Dy+``Z /&V6@ϫfM6YXx|" JNW9bwڟla6P]|#G$dt,9V 39g3WViR~Y}kꇦНHV="slNaOx8irPUړMSuʠ#W SsV^1h%wJftlxvFƭE09kS!Y0:ϡFn= ZLѿF;:oAw$&kTtꗽ6rl!4_d@k"a\RbQ,V<|.31)>V?1V45l;LA#@bd'N˸aM37).P#fr @E(fOߞtӰD|)#7Cyy[[-Ć",A͉Smr41Y'X45r3{  u| Dۢļ@<7>- ־LM݅J qdKCcC(RLș)X,/ǜu+ rhJg;8 D d7%E (]ŠlɜaBl1BVuȫaonM5*W3K4AtAJjmh!%UðЬ}+_~9|R'M'XGN2VIg4% "FbAd莨?uUy"7R1\@3};l,[ªS1.wE):Nv1`׿~N"(t7x%A)AovTIJ}|}=&iG/㓾ˌ蜵*/wk߾>$ۇ.;^kNKNN/QrZ2c_w]=h&|Xu٩ħ_{7k buO]UtjPy՛ g`4)gXrNYu!Z|r ~jY.^я6J@.<QryC;x=tno"0CMu)Olv-ѺLmhl `:lHlإσ 4x '/{ω!ԝ B0 AzB}0\*|F2E^AuXH Ydn)JJȑ L+|pD~M0G R`pzS`t6~I ʺGp^SFA1B AC7o6v#7C@c'tܭ)JJ}f pʱa??&ot}&:['I/? =Ք񧎑C~{N1ESs'Z+S Ԑ/@e35 F( mc )Mz35A4TUMk{:BbslҞ1⯛d`"‰o 'GIJ+ \%}ű^jJq7GQÕXLܥ7T-SQ .p-_Nf\ܿC,_wO 1hDW ss6^܉g3  ). eퟏLx|ܽ캂%{ikWC*v2`}'n7Tු5h^ !0@RNk뤜!)jE;caE4Ёjދc4k#ps0˜.ơƊ2^%`wxCTNRb~3ɻ4Y~O_<,2=ʗ=:u }"Yb >YT-WUOȯ [o Cza5?BˀjMM狽S+I>Do}D b+* qV >OȹOrqi,q1ݴGb)odA{c gf]n: x&8 Y7P9ڥ<(9xIŒ:zh䕭y^7E~lOvIՉ+%-d|#7fLb՚U ]6Ф&NDT҇UVWrVuƾ֮Ք9wEjI :xOE5_y5z4ӕ:VD0 ~#?>N^pMeUxggǘS"mW{<|<4Gֺ,>0W@o ݸ%6cgUL2`Q%Z6TT(9V.G1%hoslm)s1Q fpv^BP}rnJ<=>ÀȺZqIg"f/i6tMO7ĥ ӧirMaZ랻W/ raj 4SE%1/m'f%fsZM3;]V;, KVTm ոmU/ڍL7|=a*nhtq!BOiKi-DTvJ͉]heF;Y$ ]L!1&peq.T5AbqWo e_?nMݵx>`Ȗ&}J'z6]R|`Ă9'i6Fʪ^wZ+)%J6 /؏y> <>c/lTjxe)QW)c(@ D>S x῭هˠɒ[RQVu)nnǙeG67]$@m+jz97"Gaɓ5n*@2Cr3Dv`b-li5^xk}15E9CnENK%]bvPӍ6 8R.=ma5eZg:-XWE%Zzgz3f27ƀޠf8lw97sڠzpBkt,-UU-cLTT9$Ϗ;m6G3M(yfjdB]Ya^'+u$yKOOTW־dműPv}ꎑ>( yAxX\)b\qEPڬa 2D6+HU'cdmMG*c7{Ҫlj5WoƋ%buG:-)zaɊYڣ#zOYEXan4"g5;?q]iWR?^̅I;! ,[ǥIϑA%Zf"W?w`X:|Ί>˗ՆUpyr.踚e z͓L ,"^um{|(/&P)=ys,j+ESKLdDnh#\m_ZEUf5XujlE[)wb+:Y?vQ,v(%"mmMX*Z+ܹ^|oePG{ͭKjmsk;8?.uZ֦ґ8%Tm7h 4AmWc$$NRt64u+7G6NZo,3@抝x^%Sk4 %r=p:uIDcߕraUWLM=eJLUb؜OU@{E޷xb j4:춱 !H\o*j0U'4EsfEH9IkfuG-K8.Sџ2;SN 8S5.."`%jFTsLp5#,OQ(܄VUumr@pR*zuj*:tzn'Kb*7T5`paj{OU2d[_lzam67XB/?tP٩1kLl3(1KLSw~LO]e*d"F0i/yO˅/[*{btIzpwCmgh=5ḷkp XNdxU _*˨M-8ZN<ցh|HѐΤZ{#"7OTTD~"ͮ!ӸMǙMt-=هi`O)'>̌&B &V(R-nbEviJMϭg;hgl vJ4hHv61 Qj+Mw !§u3xdNm\(JfJ_Jǝe{msVmH5&d_hsNU38=(u+.  )uxv'Ln>'4hC&VKZ}J CMG]/.?ɧ l!:FH739Xa–P'@`.h#9v}`ʚZdP3PLr0 &+U(P&KoU<4w7Eb6/2eʥZbk!m5e&rnlcy״gk-\P+8'DDA)"J`[:vYaQfiЦX@ X2ؖ-)mײ9Fx9Ss;qB#f(qqi绷V Ȗ/08Wq]=}!i%&K;'Г/>jO8?2K9slWg^[E'BG\E4u|rɜ)hNd_00dL/0~/e*^;pVT渻. N!^mkqh=KhG >~}TZ ]ON\]U8Se nSj\=n6fwskTy3v|yxfaIlY qŒV1,?E/&,|(z=bl56䃎l7S4Fe7J>FfR9&cM>cJ7WuL_fzdNU ?V L%\"Ϋ[7gVuA\uj/͛9/pDEU9Lyyǻ?[ߢ|h޽/0g3C沼4UVawDK1%^>PF/²$H^bmZ eS6x٫0^z ԛDLuN*H4eMɑ_XֿuI=?q=*ec`]5lӗ]v.˥jel Lxv,߫Z^-|m򃭋W\^Rfu?:hv#gAb$^}} w`;GVa2BflNb(]9^ q~Y5.k'xe/,%g3-dZS|:vj]{yu1ͻk.q:yeQ H>0H;C8of=xR8qwB'c'Ѽ^#bPl^/|'9qU$ ZMQ1ճ3i@`6v>ĦYbAT 2"6DcHrbіe.z;ΌzC;3vQ} 8q.Mb9Ų@3'sxfrpdl=NalZ֐9Ww.1q=5cL,׽(_5:HRf!tpYN>ظphVX@A?#$Eeסo3j%pdWV2'2cٓBѢ= O-,Tulg !|aX/?VG-lNzLZS>ۏdEHFx'dwQ;o4=A9HǡGLΑXz)dlcRrA.Md]\[K@Y(s}2U(ܡ<fѦ˼[Fxnq[eqLObF[VZc`I)P "VQa"#?3Q:8)wd^ x78ޕ{!6Y(6܁nWshGRG,\&K+Q ](qm;d4 IMmt%w4#8(.kaI(+0gIXof26rM8 7"kݡ- _WԱf+<*u~|ZYg\RŒG뙣V-'kL <z7.r.e?Mm(Ƀďrs{_˵o/t >l?BiY6p:kb%Meʿx+I"N$vNζJ;7: F4FX@CaK'=AY;FcڊJKmC+Ӻ0mp+9ŷb"],n|ף ¹mzL[+h&FjtJNk5GVNT0Xpgkǭ V ~r81<=!ZaD4)MӇ d***;..B#FmR(T\ S3:f;n.N\`6ߨ2A#Nh[(TL9s8Z\H؏Nݕ.k >UEЀZ3KQTP:f*[ <cţTgto*]Riء5kmIa>nYm0fY(Fi)\C{I ^0"6y=ܒ>HD;]Ѱ:mbuEգ李U["J.GP|H{ `)δ b4ShkcH>};Dժz,CZͲh4j24gXN媨ѕY~E#]Q&6H1{Ъ U##@D}W!=n~F=}_Ffl" ЇFF쵏 ik..WD `'N),dwV.F:JjY(D9[ i2QpF2Vƶ9FDTu* C%&@Nv\8r10s h֯MaJϛ;R\?%$rMD'yfad ճ FPpQ._}䳤D$_Я?Vחw{Ѓl zR>`ROQ~Њe%"ċ5[^8 */Jʵӈ|.k 钩jf8(隆MͬK,Ko}?{P{k(p.Y!Qx>d'[K>ֆVLG]ϫWFP+ Uk;V<ʰhZ| qux&qr5j'霡 POAOTXΩ}R'J-(<"ª Tf\DY%u78鵪0aA2zy:~UTr欤 [ RXmǕY 0s>ǃ SV#/a=ÇXf.Dl˧M/q3Msٸ=׸h-iu7^W!C7-|;#6ӎO_{?+ѻ˿KxJ?cL6[x 7ҺZE%:iz4%33R1ɕؓ2G0tN?lj60/4rk}X7bHY3}7`^H~f"! ť0F u9i-yw܉ uWHIsp7Z`{+; .NPE453\>lFNc*>G=aq\XѝQy08q!9жx3Y7v=~^NƯ+Y栚tTHU_OO2^tJ^E^25E6T-V3?=8ɂ16#z!_ƿOYߴqп?qˠd5apȭ^ȳ# Z}Uڂ-kwTvŽȡ<=}y*ORWdKj>b9_j|+:xJ?vo=[zATy#dCTFeDAl9X-%d"ݧS;mj&wW<\t8]:p +=JCSܸPF!Vaa> Z dJjbNBAL}nMi0,L3{ L% NEv:=~18_-5 Y!x*^.D #E1:8H]1[&P DÚ-w^8Vc @Ӂa֒'u;Wzr\I/Q8UF~AH0U ؔ|`IAH1vܬ$E$4^G9M&)2Z߭d:/ 1RR\cѝfE.,SZy79?wqT}ƛуi6bYX𻩏@&OSaEi4s])LȒ%.*cEԢ^ۦl"Jr4l12թ4zghV* œśm͡_TD0lr3^"lf]<2Z(,^Lv KbebMo6u.K8&mixTVfąU |d@rt)}-alUlqAEe8U Zi,YӶ b? Y/A8lH&.wz,oiOբzO{M{)!>҅Ϙ!X #>Fȿ 4L1JhxsÖ6_L4yx5j>c"\KaB$9J|K0T/)Z06$ewIX,G>]0jȃYW*䭃Rl9'Dy02}C{/i] ҵ̨u3ⓙ|>j.RW3FFeĢnwo 1)U, +EY^ME Xй*/fEUp@=/et0 \taŨxZR "8AFmۦ|Ǩ5Gsps=`{t*d(P*VZHbVwL>79.?PcyֽPKGˆ̲$o2-q yŽ=_L)/xʐ?= XÜk0%%-?IuhAzǪDMQ4'[޲9fq@86`a׌JM)\J.f,Y>) wN +\ƔӅ<хZ|noeGXZ%SC4`rvȘ97Iv6O,iUM"<EMsVcۥ:k$:LtnՃcPӃXN@@w%.G0T J"G/p^y\Ɣi:ݣw}slйK!W#1[:̲ܞ qx1`+qg﯆z'W '$Ae0iyDZʒ :i }w ʪ\(G8\$ &rlmujٷƍŚ 5QGs| 3Ep߈vk立 ^P91эVxPˏbS?|Ti8._h0ٖH50c&T1OL6*H`[c隰[ph2'LKA$_2D4o ^r:7);k6d&=p`#|jQ(j_s3Jt(L/r  J7ٮ[b)j+?SV:aLcwp{skU!.CVSm{,\kPvEUVG 9eeQG;zRֲGTjR݂XC>:Y@׈tF.:[Is!+-o.7^ .=>N/_OK8ʫx>n9\uIO/o./7Οϗ'ʑUq],/]ײ0 :zz8QnZJ(ϗ/Uj 5HSޜ/?oүNqųUEdf̹ș$S^?ߨ2\VXΦI@^`+3ɘeӻ&#PDnxn9[T3+^_ꪎlƭ{r/&S,.Zy#O*W!D2QfǍZ"nRrԧB^p{B>Rb ֦- ǀVPCQ*yU۵r d B_5Uk%LzG3&) :86Ppt<"AWjIM-1imCJ/4ɲ(s2fXǛ."ʌ{A9#zK g&GP'X+{F6`04}4 :Tz)*7UN]+I ] #]}]sr>;)9hf]x;AF׫:;8*0ZBarA7ElM})ѲŨz%h\F)u,Ola`ͳwɎ9#"lP | HMnkx 663&Cq\1:Ee eoi\(e]2b:4)5u jI2:-9 *S\"b mhnrJhn=VhhOȇ|U߀FmdT3:#Ur/1s'G{kHv5eEb8escRQVa_9DD6)-3䡡y<8@\2y`%R_k <#īC/ci|t4U¾XT4씹֐8<iےn3_0]8*U:\*_]{o {NO7\^T<< ȳ' mA{W5S(1F΅]I&pfznwwհVz3yP){˶{h1&5_AN@;ʶ"Mu%4 ࢒G,u;:,dZ)Eb6N7; V7r3@eؤ=779O0|6cZkEYKS^䕤,661vӂ+lu-[:Y^&xȜZfnūUI}0nBJe1?@Miww`0,N"EYmZ69w:,nm7O5L@U؀ʣroi 9)9&G&w[G ύjT~;DCϧjtxbOZ\1= )R,7h,bb4yZN#_|"UUr_ܲ86{fMB=tp$3,f?6s-sG?Xdٺ44V*iJ@tlro!7J4Oh>ih8n؀x7瓲wCOt6Fm f[?8\áS&`fH8@QGlrsCº~[CɌZ1U`kkFq&cUuBO,̠V :S#ūVjuiZ}r9}{}y$ bڲ o~=u 'W]ڷoI*筪F@nuKQ÷\z|t8CS6R0MK3QI gh5w"Kתd7ZWxKz*/Xk{"Ȋ~}y՗Gcq2Uϗ<(_їȡ)bv|9i \KdTO~5mgƼd)ͩx~:?EM43ٔ<8חn?\_s>ބ^6ᱚ,NKHx(Oas`Rg0]U*́y>H l28sRj$@dZpE TH >D|}S%a!PoZڎ1۱]2,D6;ZNRBcށ t\{1;CM cP+TDi+ CsUlFQhbǥ{V8ojḀR-.|"؆ov/&Q>eӂ ҒڶlMVB}xm1WFwm1cvA#jywGwq`h*m8}6bC $ƻvCmWp%?pC`rDUIE Ȧw 6'cc_ۍ.VlR.5'0}W,k\lvAtLz66õs+R=gcyh"bzubiZdICE1f D3tq`wٵx\@eY :]/"n"]*> (jx[%xwlr?MfC<\?i^|7`x1,E, 4xN]%h]^}WIhRhxjJTw=DtX3:XF(c!/0C c3c5 3(' z DΏ8`CYX@ l3:/*whEWo\ú(T!21#]\𖶥)AԬ5Aٷ9F ߈ŎQu"Br~h8jƸ Rw?`c<Zh"7d_dS/?oNVd-S^nߞ_~^^/C?t]߮߯>E|Ur]۴r, !o ̀A>Le~26߭܍{mJxy~g>1eMhp$S9f4ϧM;;@-e[AC;0Ƕ,o:JC|vYūZj@k- ]\Y|Y88$_ʃ_߾("D}yd$7}ls3Ihkؖ<d)P-,LJצپ; u#򩫌*2le3DZxP4MGV nFȡ]+|cHE_﹎32x֥fBOoA]h wߛMh- BUD;T&-H+zGwj˅`#| o+>R!ZUAΫgKڄi-qyay;r1dx0׵imv}ZSK_IڌŮi:Y}Ts)xZ׀d^]t2kyeZ?ysֲXxZho9+`GL'gx(TL=Kl&-;¿N|y)6Idz#&w|Dc q$m瓞-n|OS=$8Odm ]H0|6ȡ=qҺ4tTgק* tKZH&Pj?:^LJn=ݿyR:DqH<cz hdW.u#mtpw_OZ0*NjYלKJSI/>?R?\!Z7퉫\ӵ1U?&UOw=%r,ḵ!IBeeO_VC0;iYjyuk^"pgXB5âSɫ5{ݺpnzkwl{ E[˦i]E4 taG鉫]@_ߞ_Kgz"kŷ7. >,-éN䥟iFkmIWVv;j87hod/qPS黥uދMf$g#|r :YE59g(5JzslS+ҖpQl;JGfr2}S7$%@eGZpi@dHzBa|v X2LQ]eo-=Ӣ@[w{_DJ|fG3%nJ ]S_=_kUL`.Q4|*է/+Pc&o:y?+)Ny X/'t௉{ n(+W0nsM rW!Mj9)oyW_Hahۤphjp"qN+ąlDu5Y2sA Ax mgsDʹml SA%iKZ ?+< f=_w6(Ϝsm@.aC'ܒd ]ߓWRlUB)M^~tb)vC1ϼݟOw/?t'Rîs!"/6)+*ɦ<-ZaˤPVzb+Pv[>!AѲ伢 (JdGWS+5)5V7Rמ''脢΋z<]IwuTn%r)@ &gx.>O~'=nV~vb?C3yG&HjŎTv}V1殛>lw($VL`]7hu<=8^utncd\]#nvuv"8흖VQ2lB d{$58ݗ%P3qm ⰴ,>s\Li%RTљ+ܭ}8RQﶳ8hP!.f$3#[G/jərPXYT++q|T]ҚГr䔵j-yJ<0Y WL͇\5v)c 8c쐩9h3qHOu#. (M >r M{V0(r p1+Moc2$@zE`2 EY0p[Wзځ:j#h\vwPFpQRǚTg(Chۤ=?ÿ\!˱{(('~O8&!Rx@$kÃbTَR y$)D>Gԃ31Ƀ-s+i)I3~Yޜ؟ݘ&jMVwWz:: ک2+Zٱj50˧ BWsT?e֩EKb~Q;%Cޡ`OJZ&I"_3?-aZ34,Lz*i]PIĠ£L7({)V2 TRy&3N&N TBF $'P,ֈ3}mYJ9Ld<IEᘰ=n&!\RrI RYP'U;c?_Ղ|d|[u*2_OD.U_sk}6HGV Wjzt#"IjgY].U|h U'MkSU|kJJe^Ph`*E7fV<Ѷ,Zk~o,_68\T=px֚ yK*>qz}j#Qs]Ts qd3[ DVj'68e]_WWPۆ`⻻_o߭/ /p\Okf!qy&qʍ kV N~32Mzo|12N̶Y꼿n-֫*,j"haת 9{EQ,[m=e9)Ѷ%1/6D9!kx_/O_wZ3wUzdoޔ&/ƝP`P('&PpRƤP"Ǭ!N$@YʹѐCFEʮ > #,e L]LbZ;0AS:ޖr ԫ',RhTdc85rݩanz# vtԵZÓSOd8t4RZK`Vjlۮ:?=6*Kv8=RepL]xҦJ OBĕ륖qplӤԲWoAEhSB g*}f*@kj3*CT >byw"m\һMN4 7YQ 9N]M-J[QJ뭵.@Ҡ x3#)HHu!1f*A l#b,AψKzӐ؀ĮآkM S&x ؎1iM(hҷldC}&466 shhӪ$Vf=Ӽ Ǯ!Ǯ(K*].krKǢ7ԍ,Rceߴm\ !lc]E/`$iazYHAQ??fvtP]=x]GӠ-^Ҟq$B43 40,ɶƺPI1vUȒ:gf33+kɪ$M&NUu\KCYF %4HQP\xy[@7i̯jDAL{>(bA^L̋AG>%2~תXV.z$4^k|z*^zE3#<8ġŗ.iio7FܳXS-J6|d,q]\3_0(jLQ)^M3<4,C!,*ִM<8"`hs*P19噕%.i7'"$^ 4OQ0E$+M_7_futUF"$xJQ)Yqln,`ٸa#|s|8 Lq"L8d ,S:| GAasHp ΅A%zfv#m@u,kVm/t,.IuS=cdq*e'ͯK'-::vl36Myy@m:cAVbpTA"b =ݮvuz:-G(Iٮ<4%,뢲A 2: &DѨ⥆/ϯO7O/ۃg KoDq׀mdpѥ3!U޾lo@=ԭN*fEE~H A%N|b.XI9U-B(tI+`,muۓEYp@|hvIa3^w 3Ɋ>:Vx]WKn獡nJ6aTd*t1pP̭OqZ}UOSCĮۅO3tКsn_6owfuж0߿lv{B}߼n5f8>ΚU ̜/10_%Kf07Ӣ1H\d>aй͂I,f<fѩYt%0,6qNR} H<8v'J8ٴ@V@I1sW%4n| V.*r~7 ๹8 sb!aI(Ji[PLk:${GΔ{[f>ݱ[+ C.SkC8Yg^p0Oo߶/2U IUsJ޸b؝&1$ w cL< ?хpqlNf{ʐ?/g<ً:u=+ R7&fYpO4oUqNB1AVV GnQ !EnKrq݃ /?/Ι-3-cBcC+H\O /g?=zǞc%8mgg8 ?2"4"Xq:vӲblrs^؆C$U4dfֆqb'|Z/Y"P:X+?8Xoe,7ȇq%|o+ڐIQI"PW;8AZ8G3T-y뾂xiM/LڴuE]ayN+ uJ$s61HEZ,n&@4_S u,äh,0Z;Rp. xb>ZEL/ d\Lv~2qFbpzW!9l |)<*h̏d8O5x{j2[Dy)m50u 2d8 ޳Mǣ]ucUh #b YBk" %Iغz7K&u n+lElC<`JOJ%ZZIgߴ)K{0 `]e)5f@Qdo9kPRcTؘ*ʯr[P÷݀r5>/wf Bs"m FJ6 iU@7 `.$!t@ҦCL?ةtu;35',l_C10 ?ef4eܫx7QC7/-jGڅ~HO2<zJPB0N' %砋>`  A8%!f(͜N" C:/A8thhkI w(UR`XI۰H-iю2##FE?7o?^1Am^?"qGBD׉ 5l?FUIqS$ѳTc]u3:Y*wv+Q[=,c)>2dtiacwZiմV0ց %U ,dr(J2s`TO!FMl[FpOY\c bZ/R*ajZߍmp6,_jsdbf~b2 *K,cb?VQqii#SP+zAe R݋ AQ\%N#cKXXLf33-(9? 0y%d`Lw>?%=lw VO3.z7@Pe:h6 R"R\5̩ᴿ^u8޺`wlKܚQ-AnFt%SX@jI! w?ȴ]J/#Mn$ŝLYT7.. 8}bKjJyr dSBMu&dCiD̄m.SܱeQI=KǝX F%*kM"lGg$%8W)pAX*f z@r!1WNYBF@f<(O iж./%?xA?{hB;F[Kw/oG=rՕvi {B>\<š\i$ #"?U4 CJ44GkW;/Q6+<`&RlѾdR=89Gp$ۢ^DucKW'&"PY*kFcCi]ZO/ Tz LzYOcCG\|R.%g;vI;ڟxzm!IGh~N2*Jnz,?DR(ĴSHw J>tHP[Aw %3u8)kbWNtkD0-1k$ΒwY[h: HtDMvA@*Gtd_Φi0|&U̝EPT,ay7.pϻ澖/ %}Xb8TJuwnJpǸzȢț'ggZ$8(<ֵR@]ѐ`29궗vښ;j34Y@tP<=>O7=Kiy5'0!^EoɐHN4FV5j"[NHmUEV]2;C!QQ8SFHWXE;o0hA ou2̑'-ٔ'}hA-Cj,$)w̱OE|H~uQ$iK#^?zoi2lV9WY>?~Yz؁.sVbz *6?eu| ǘ ӕΕ;/MEd3^_wS^-"*HԼd]9ʹݙH&w\q.:2 y0Oe]:@-|u61lh[q&$fx!TYnK *Vqx3TUpo9, }W0Ɯ{+@Ɗ+x~u-9Q l|%F9*9s0ўcp]8Eñ柇uQ $gD&&ɴ@BֵD6mrb+鱳*(Ng'VJA9uq;IC.z{!J_T]{VoQ__WItSHo37RW>d5u6 N5ZW\T\,K kՀ'5+rf) Kќ_rK1`Sb.&4rr@n!/|)-re<K63Gaq v]%3VvB;:H5dkgՂy72Z=i;kWܢzu_ӍHC{C20Gc$ gP`Y|- q0=Ȉq՜EFgiZc$xBQd&+ӧ f_z*L+2at{m/i9(+Ou#ɧodh?Y$IBw]rduas%= 1R+-G۪\*⼊4psae~? FӞwRa 2N!$3D n/M*[Ƞ$ޜpp}1#+w T"e3,J:вoE)(ga~y4a;;Y:?Cs&gĩUjP^ۥiKVkYσs@yC-b!`d^I"٣(v<˜ h32>`T{{}jq 2$,81eU8 ^Y~pgolang-github-cilium-ebpf-0.21.0+ds1/internal/kconfig/testdata/test.kconfig000066400000000000000000000003111520243672000265600ustar00rootroot00000000000000CONFIG_TRISTATE=m # CONFIG_IS_NOT_SET is not set CONFIG_BOOL=y CONFIG_CHAR=100 CONFIG_USHORT=30000 CONFIG_INT=123456 CONFIG_ULONG=0xDEADBEEFC0DE CONFIG_STR="abracad" CONFIG_FOO="foo" CONFIG_FOO="bar" golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/000077500000000000000000000000001520243672000221525ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/auxv.go000066400000000000000000000025061520243672000234670ustar00rootroot00000000000000package linux import ( "fmt" "io" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/unix" ) type auxvPairReader interface { Close() error ReadAuxvPair() (uint64, uint64, error) } // See https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/auxvec.h const ( _AT_NULL = 0 // End of vector _AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image ) type auxvRuntimeReader struct { data [][2]uintptr index int } func (r *auxvRuntimeReader) Close() error { return nil } func (r *auxvRuntimeReader) ReadAuxvPair() (uint64, uint64, error) { if r.index >= len(r.data)+2 { return 0, 0, io.EOF } // we manually add the (_AT_NULL, _AT_NULL) pair at the end // that is not provided by the go runtime var tag, value uintptr if r.index < len(r.data) { tag, value = r.data[r.index][0], r.data[r.index][1] } else { tag, value = _AT_NULL, _AT_NULL } r.index += 1 return uint64(tag), uint64(value), nil } func newAuxvRuntimeReader() (auxvPairReader, error) { if !platform.IsLinux { return nil, fmt.Errorf("read auxv from runtime: %w", internal.ErrNotSupportedOnOS) } data, err := unix.Auxv() if err != nil { return nil, fmt.Errorf("read auxv from runtime: %w", err) } return &auxvRuntimeReader{ data: data, index: 0, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/auxv_test.go000066400000000000000000000047351520243672000245340ustar00rootroot00000000000000package linux import ( "encoding/binary" "errors" "fmt" "os" "testing" "unsafe" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) type auxvFileReader struct { file *os.File order binary.ByteOrder uintptrIs32bits bool } func (r *auxvFileReader) Close() error { return r.file.Close() } type auxvPair32 struct { Tag, Value uint32 } type auxvPair64 struct { Tag, Value uint64 } func (r *auxvFileReader) ReadAuxvPair() (tag, value uint64, _ error) { if r.uintptrIs32bits { var aux auxvPair32 if err := binary.Read(r.file, r.order, &aux); err != nil { return 0, 0, fmt.Errorf("reading auxv entry: %w", err) } return uint64(aux.Tag), uint64(aux.Value), nil } var aux auxvPair64 if err := binary.Read(r.file, r.order, &aux); err != nil { return 0, 0, fmt.Errorf("reading auxv entry: %w", err) } return aux.Tag, aux.Value, nil } func newAuxFileReader(path string, order binary.ByteOrder, uintptrIs32bits bool) (auxvPairReader, error) { // Read data from the auxiliary vector, which is normally passed directly // to the process. Go does not expose that data before go 1.21, so we must read it from procfs. // https://man7.org/linux/man-pages/man3/getauxval.3.html av, err := os.Open(path) if errors.Is(err, unix.EACCES) { return nil, fmt.Errorf("opening auxv: %w (process may not be dumpable due to file capabilities)", err) } if err != nil { return nil, fmt.Errorf("opening auxv: %w", err) } return &auxvFileReader{ file: av, order: order, uintptrIs32bits: uintptrIs32bits, }, nil } func newDefaultAuxvFileReader() (auxvPairReader, error) { const uintptrIs32bits = unsafe.Sizeof((uintptr)(0)) == 4 return newAuxFileReader("/proc/self/auxv", internal.NativeEndian, uintptrIs32bits) } func TestAuxvBothSourcesEqual(t *testing.T) { runtimeBased, err := newAuxvRuntimeReader() skipIfNotSupportedOnOS(t, err) if err != nil { t.Fatal(err) } fileBased, err := newDefaultAuxvFileReader() if err != nil { t.Fatal(err) } for { runtimeTag, runtimeValue, err := runtimeBased.ReadAuxvPair() if err != nil { t.Fatal(err) } fileTag, fileValue, err := fileBased.ReadAuxvPair() if err != nil { t.Fatal(err) } if runtimeTag != fileTag { t.Errorf("mismatching tags: runtime=%v, file=%v", runtimeTag, fileTag) } if runtimeValue != fileValue { t.Errorf("mismatching values: runtime=%v, file=%v", runtimeValue, fileValue) } if runtimeTag == _AT_NULL { break } } } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/cpu.go000066400000000000000000000017541520243672000232770ustar00rootroot00000000000000package linux import ( "fmt" "os" "strings" ) func ParseCPUsFromFile(path string) (int, error) { spec, err := os.ReadFile(path) if err != nil { return 0, err } n, err := parseCPUs(string(spec)) if err != nil { return 0, fmt.Errorf("can't parse %s: %v", path, err) } return n, nil } // parseCPUs parses the number of cpus from a string produced // by bitmap_list_string() in the Linux kernel. // Multiple ranges are rejected, since they can't be unified // into a single number. // This is the format of /sys/devices/system/cpu/possible, it // is not suitable for /sys/devices/system/cpu/online, etc. func parseCPUs(spec string) (int, error) { if strings.Trim(spec, "\n") == "0" { return 1, nil } var low, high int n, err := fmt.Sscanf(spec, "%d-%d\n", &low, &high) if n != 2 || err != nil { return 0, fmt.Errorf("invalid format: %s", spec) } if low != 0 { return 0, fmt.Errorf("CPU spec doesn't start at zero: %s", spec) } // cpus is 0 indexed return high + 1, nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/cpu_test.go000066400000000000000000000010031520243672000243210ustar00rootroot00000000000000package linux import ( "testing" ) func TestParseCPUs(t *testing.T) { for str, result := range map[string]int{ "0-1": 2, "0-2\n": 3, "0": 1, } { n, err := parseCPUs(str) if err != nil { t.Errorf("Can't parse `%s`: %v", str, err) } else if n != result { t.Error("Parsing", str, "returns", n, "instead of", result) } } for _, str := range []string{ "0,3-4", "0-", "1,", "", } { _, err := parseCPUs(str) if err == nil { t.Error("Parsed invalid format:", str) } } } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/doc.go000066400000000000000000000001221520243672000232410ustar00rootroot00000000000000// Package linux contains OS specific wrappers around package unix. package linux golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/helper_test.go000066400000000000000000000006521520243672000250220ustar00rootroot00000000000000package linux import ( "errors" "testing" "github.com/cilium/ebpf/internal" ) // skipIfNotSupportedOnOS is a copy of testutils.SkipIfNotSupported to avoid // a circular dependency. func skipIfNotSupportedOnOS(tb testing.TB, err error) { tb.Helper() if err == internal.ErrNotSupportedOnOS { tb.Fatal("Unwrapped ErrNotSupportedOnOS") } if errors.Is(err, internal.ErrNotSupportedOnOS) { tb.Skip(err.Error()) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/kconfig.go000066400000000000000000000013101520243672000241140ustar00rootroot00000000000000package linux import ( "fmt" "os" ) // FindKConfig searches for a kconfig file on the host. // // It first reads from /boot/config- of the current running kernel and tries // /proc/config.gz if nothing was found in /boot. // If none of the file provide a kconfig, it returns an error. func FindKConfig() (*os.File, error) { kernelRelease, err := KernelRelease() if err != nil { return nil, fmt.Errorf("cannot get kernel release: %w", err) } path := "/boot/config-" + kernelRelease f, err := os.Open(path) if err == nil { return f, nil } f, err = os.Open("/proc/config.gz") if err == nil { return f, nil } return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path) } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/platform.go000066400000000000000000000015031520243672000243240ustar00rootroot00000000000000package linux import ( "runtime" ) // PlatformPrefix returns the platform-dependent syscall wrapper prefix used by // the linux kernel. // // Based on https://github.com/golang/go/blob/master/src/go/build/syslist.go // and https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L10047 func PlatformPrefix() string { switch runtime.GOARCH { case "386": return "__ia32_" case "amd64", "amd64p32": return "__x64_" case "arm", "armbe": return "__arm_" case "arm64", "arm64be": return "__arm64_" case "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le": return "__mips_" case "s390": return "__s390_" case "s390x": return "__s390x_" case "riscv", "riscv64": return "__riscv_" case "ppc": return "__powerpc_" case "ppc64", "ppc64le": return "__powerpc64_" default: return "" } } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/statfs.go000066400000000000000000000010201520243672000237760ustar00rootroot00000000000000package linux import ( "unsafe" "github.com/cilium/ebpf/internal/unix" ) func FSType(path string) (int64, error) { var statfs unix.Statfs_t if err := unix.Statfs(path, &statfs); err != nil { return 0, err } fsType := int64(statfs.Type) if unsafe.Sizeof(statfs.Type) == 4 { // We're on a 32 bit arch, where statfs.Type is int32. bpfFSType is a // negative number when interpreted as int32 so we need to cast via // uint32 to avoid sign extension. fsType = int64(uint32(statfs.Type)) } return fsType, nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/statfs_test.go000066400000000000000000000006631520243672000250510ustar00rootroot00000000000000package linux import ( "testing" "github.com/cilium/ebpf/internal/unix" "github.com/go-quicktest/qt" ) func TestFSType(t *testing.T) { for _, fs := range []struct { path string magic int64 }{ {"/sys/kernel/tracing", unix.TRACEFS_MAGIC}, {"/sys/fs/bpf", unix.BPF_FS_MAGIC}, } { fst, err := FSType(fs.path) skipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(fst, fs.magic)) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/testdata/000077500000000000000000000000001520243672000237635ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/testdata/auxv32le.bin000066400000000000000000000002601520243672000261240ustar00rootroot00000000000000 P5!03d4L  P L   Νߝ ϝgolang-github-cilium-ebpf-0.21.0+ds1/internal/linux/testdata/auxv64le.bin000066400000000000000000000005001520243672000261260ustar00rootroot00000000000000d@ U8 PZz U e e ggYX{7_{7iX{7!P~7golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/testdata/auxv64le_no_vdso.bin000066400000000000000000000004601520243672000276620ustar00rootroot00000000000000d@ U8 PZz U e e ggYX{7_{7iX{7golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/testdata/vdso.bin000066400000000000000000000074101520243672000254320ustar00rootroot00000000000000ELF>@ @8@< <   TTPtdDD   48Fe ~UqR0hK x|Cn*&&bemX("  @=" 6 " @ " ! hJ %Q" %__vdso_gettimeofday__vdso_time__vdso_clock_gettime__vdso_clock_getres__vdso_getcpulinux-vdso.so.1LINUX_2.6 XuhX oh r ohooJLinux|LinuxGNUB2:Or!G=k;@l\,|4LzRx <nAT Ec GBA A [ BEA F \8H4ptAC BHI BA A R BA A 4AC BEs DA A H IA A @ 8%UHcIIHL7HAUATS7@uGttu[A\A]]fH H I@(HOHxD_H9s H)IHЋOMX 9uHH=ɚ;v1H-ʚ;H=ɚ;wI[A\MA]]I1L%A$xL-LfA $9uH H ILcHA+fH H HuH+ZIĉIHL PHIHHH H 2A9uHfw1f੃tHt%H1HVè`u uú =1UHATISHHt8HHUHM1H=u+HEHEHiMbH&HC1MuH[A\]HL`H[A\]ËA$AT$HHtHUHAUIATAw1੃t4HNHH=mhuA\1A]]DLA\A]]è`utHNHH=!,HcH HHHuH1IuHqIu29t@[{Ht Ht 1WrO;sErf=;3r+;!111GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0.shstrtab.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_d.dynamic.note.eh_frame_hdr.eh_frame.text.altinstructions.altinstr_replacement.comment  D ohhP  r%oJJ2ohh8A JTPD^XXhEn [ 0< *f golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/testdata/vdso_multiple_notes.bin000066400000000000000000000200001520243672000305430ustar00rootroot00000000000000ELF>p@P @8@  HHPtdLL    A Cnx|'be R0hK~UqmX ! W" `j" Tw * `6 TJ" \" " *__vdso_gettimeofday__vdso_time__vdso_clock_gettime__vdso_clock_getresgettimeofdaytimeclock_gettimeclock_getres__vdso_getcpugetcpulinux-vdso.so.1LINUX_2.6 u  o o~ooLinuxLinuxGNU/VbXp;LhH(P(hzRx $(TAC { A S $D`AC Gh A lAC P $AC Cw A $@TAC { A S $xUAC IG *AC e UHw.ƒtH@B`uHu ]1HtHHN]ff.UHAVSHIHtQHHEHEH=HU1xt`HL HEHEHiMbH&HC1Mu H[A^]Ë A ANUHH5HtH]Ðf.UHAVSIwu੃tH=L1uL[A^]è`t?HH HH uHIHAIF519t٨uLH=UUHw.9ƒtH@B`uHu ]1HtHHN]ff.UHAWAVSHIHcHL I(DAtGAtfH H Hy|5 6fH H H+D5 HHHHHLuHHeH H 9uHHxvM9HG_OMq7D93HH)HE1H9IFLHHuHʚ;r'1f.H6eHu؃Hɚ;w1IM0IpDH[A^A_]Ð @L$ÐUH{Ht Ht 1]Ú=r5;+r#;11Linker: LLD 11.0.0 (/var/cache/chromeos-cache/distfiles/host/egit-src/llvm-project a8e5dcb072b1f794883ae8125fb08c06db678d56)Chromium OS 11.0_pre391452_p20200527-r7 clang version 11.0.0 (/var/cache/chromeos-cache/distfiles/host/egit-src/llvm-project a8e5dcb072b1f794883ae8125fb08c06db678d56).shstrtab.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_d.dynamic.rodata.note.eh_frame_hdr.eh_frame.text.altinstructions.altinstr_replacement.comment  ` oP %o~~2o8AJRHXLf((<pppv* * Ak k 0 % golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/vdso.go000066400000000000000000000075221520243672000234620ustar00rootroot00000000000000package linux import ( "debug/elf" "encoding/binary" "errors" "fmt" "io" "math" "os" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) var ( errAuxvNoVDSO = errors.New("no vdso address found in auxv") ) // vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library // linked into the current process image. func vdsoVersion() (uint32, error) { av, err := newAuxvRuntimeReader() if err != nil { return 0, err } defer av.Close() vdsoAddr, err := vdsoMemoryAddress(av) if err != nil { return 0, fmt.Errorf("finding vDSO memory address: %w", err) } // Use /proc/self/mem rather than unsafe.Pointer tricks. mem, err := os.Open("/proc/self/mem") if err != nil { return 0, fmt.Errorf("opening mem: %w", err) } defer mem.Close() // Open ELF at provided memory address, as offset into /proc/self/mem. c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64)) if err != nil { return 0, fmt.Errorf("reading linux version code: %w", err) } return c, nil } // vdsoMemoryAddress returns the memory address of the vDSO library // linked into the current process image. r is an io.Reader into an auxv blob. func vdsoMemoryAddress(r auxvPairReader) (uintptr, error) { // Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`, // the address of a page containing the virtual Dynamic Shared Object (vDSO). for { tag, value, err := r.ReadAuxvPair() if err != nil { return 0, err } switch tag { case _AT_SYSINFO_EHDR: if value != 0 { return uintptr(value), nil } return 0, fmt.Errorf("invalid vDSO address in auxv") // _AT_NULL is always the last tag/val pair in the aux vector // and can be treated like EOF. case _AT_NULL: return 0, errAuxvNoVDSO } } } // format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)' type elfNoteHeader struct { NameSize int32 DescSize int32 Type int32 } // vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in // the ELF notes section of the binary provided by the reader. func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) { hdr, err := internal.NewSafeELFFile(r) if err != nil { return 0, fmt.Errorf("reading vDSO ELF: %w", err) } sections := hdr.SectionsByType(elf.SHT_NOTE) if len(sections) == 0 { return 0, fmt.Errorf("no note section found in vDSO ELF") } for _, sec := range sections { sr := sec.Open() var n elfNoteHeader // Read notes until we find one named 'Linux'. for { if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil { if errors.Is(err, io.EOF) { // We looked at all the notes in this section break } return 0, fmt.Errorf("reading note header: %w", err) } // If a note name is defined, it follows the note header. var name string if n.NameSize > 0 { // Read the note name, aligned to 4 bytes. buf := make([]byte, internal.Align(n.NameSize, 4)) if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil { return 0, fmt.Errorf("reading note name: %w", err) } // Read nul-terminated string. name = unix.ByteSliceToString(buf[:n.NameSize]) } // If a note descriptor is defined, it follows the name. // It is possible for a note to have a descriptor but not a name. if n.DescSize > 0 { // LINUX_VERSION_CODE is a uint32 value. if name == "Linux" && n.DescSize == 4 && n.Type == 0 { var version uint32 if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil { return 0, fmt.Errorf("reading note descriptor: %w", err) } return version, nil } // Discard the note descriptor if it exists but we're not interested in it. if _, err := io.CopyN(io.Discard, sr, int64(internal.Align(n.DescSize, 4))); err != nil { return 0, err } } } } return 0, fmt.Errorf("no Linux note in ELF") } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/vdso_test.go000066400000000000000000000036341520243672000245210ustar00rootroot00000000000000package linux import ( "encoding/binary" "errors" "os" "testing" "github.com/go-quicktest/qt" ) func TestAuxvVDSOMemoryAddress(t *testing.T) { for _, testcase := range []struct { source string is32bit bool address uint64 }{ {"auxv64le.bin", false, 0x7ffd377e5000}, {"auxv32le.bin", true, 0xb7fc3000}, } { t.Run(testcase.source, func(t *testing.T) { av, err := newAuxFileReader("testdata/"+testcase.source, binary.LittleEndian, testcase.is32bit) if err != nil { t.Fatal(err) } t.Cleanup(func() { av.Close() }) addr, err := vdsoMemoryAddress(av) if err != nil { t.Fatal(err) } if uint64(addr) != testcase.address { t.Errorf("Expected vDSO memory address %x, got %x", testcase.address, addr) } }) } } func TestAuxvNoVDSO(t *testing.T) { // Copy of auxv.bin with the vDSO pointer removed. av, err := newAuxFileReader("testdata/auxv64le_no_vdso.bin", binary.LittleEndian, false) if err != nil { t.Fatal(err) } t.Cleanup(func() { av.Close() }) _, err = vdsoMemoryAddress(av) if want, got := errAuxvNoVDSO, err; !errors.Is(got, want) { t.Fatalf("expected error '%v', got: %v", want, got) } } func TestVDSOVersion(t *testing.T) { _, err := vdsoVersion() skipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) } func TestLinuxVersionCodeEmbedded(t *testing.T) { tests := []struct { file string version uint32 }{ { "testdata/vdso.bin", uint32(328828), // 5.4.124 }, { "testdata/vdso_multiple_notes.bin", uint32(328875), // Container Optimized OS v85 with a 5.4.x kernel }, } for _, test := range tests { t.Run(test.file, func(t *testing.T) { vdso, err := os.Open(test.file) if err != nil { t.Fatal(err) } defer vdso.Close() vc, err := vdsoLinuxVersionCode(vdso) if err != nil { t.Fatal(err) } if vc != test.version { t.Errorf("Expected version code %d, got %d", test.version, vc) } }) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/version.go000066400000000000000000000016661520243672000241770ustar00rootroot00000000000000package linux import ( "fmt" "sync" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) // KernelVersion returns the version of the currently running kernel. var KernelVersion = sync.OnceValues(detectKernelVersion) // detectKernelVersion returns the version of the running kernel. func detectKernelVersion() (internal.Version, error) { vc, err := vdsoVersion() if err != nil { return internal.Version{}, err } return internal.NewVersionFromCode(vc), nil } // KernelRelease returns the release string of the running kernel. // Its format depends on the Linux distribution and corresponds to directory // names in /lib/modules by convention. Some examples are 5.15.17-1-lts and // 4.19.0-16-amd64. func KernelRelease() (string, error) { var uname unix.Utsname if err := unix.Uname(&uname); err != nil { return "", fmt.Errorf("uname failed: %w", err) } return unix.ByteSliceToString(uname.Release[:]), nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/linux/version_test.go000066400000000000000000000006241520243672000252270ustar00rootroot00000000000000package linux import ( "testing" "github.com/go-quicktest/qt" ) func TestCurrentKernelVersion(t *testing.T) { _, err := KernelVersion() skipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) } func TestKernelRelease(t *testing.T) { r, err := KernelRelease() skipIfNotSupportedOnOS(t, err) if err != nil { t.Fatal(err) } if r == "" { t.Fatal("unexpected empty kernel release") } } golang-github-cilium-ebpf-0.21.0+ds1/internal/math.go000066400000000000000000000020461520243672000222750ustar00rootroot00000000000000package internal // Align returns 'n' updated to 'alignment' boundary. func Align[I Integer](n, alignment I) I { return (n + alignment - 1) / alignment * alignment } // IsPow returns true if n is a power of two. func IsPow[I Integer](n I) bool { return n != 0 && (n&(n-1)) == 0 } // Between returns the value clamped between a and b. func Between[I Integer](val, a, b I) I { lower, upper := a, b if lower > upper { upper, lower = a, b } val = min(val, upper) return max(val, lower) } // Integer represents all possible integer types. // Remove when x/exp/constraints is moved to the standard library. type Integer interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr } // List of integer types known by the Go compiler. Used by TestIntegerConstraint // to warn if a new integer type is introduced. Remove when x/exp/constraints // is moved to the standard library. var integers = []string{"int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr"} golang-github-cilium-ebpf-0.21.0+ds1/internal/math_test.go000066400000000000000000000024471520243672000233410ustar00rootroot00000000000000package internal import ( "fmt" "go/importer" "regexp" "slices" "strings" "testing" "github.com/go-quicktest/qt" ) func TestPow(t *testing.T) { tests := []struct { n int r bool }{ {0, false}, {1, true}, {2, true}, {3, false}, {4, true}, {5, false}, {8, true}, } for _, tt := range tests { t.Run(fmt.Sprintf("%d", tt.n), func(t *testing.T) { if want, got := tt.r, IsPow(tt.n); want != got { t.Errorf("unexpected result for n %d; want: %v, got: %v", tt.n, want, got) } }) } } func TestIntegerConstraint(t *testing.T) { rgx := regexp.MustCompile(`^(u)?int([0-9]*|ptr)?$`) pkg, err := importer.Default().Import("reflect") if err != nil { t.Fatal(err) } for _, name := range pkg.Scope().Names() { name = strings.ToLower(name) if !rgx.MatchString(name) { continue } if !slices.Contains(integers, name) { t.Errorf("Go type %s is not in the list of known integer types", name) } } } func TestBetween(t *testing.T) { tests := []struct { val, a, b, r int }{ {0, 1, 2, 1}, {1, 1, 2, 1}, {2, 1, 2, 2}, {3, 1, 2, 2}, {4, 10, 5, 5}, {11, 10, 5, 10}, } for _, tt := range tests { t.Run(fmt.Sprintf("%d between %d and %d", tt.val, tt.a, tt.b), func(t *testing.T) { qt.Assert(t, qt.Equals(Between(tt.val, tt.a, tt.b), tt.r)) }) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/output.go000066400000000000000000000037061520243672000227100ustar00rootroot00000000000000package internal import ( "bytes" "errors" "go/format" "go/scanner" "io" "reflect" "strings" "unicode" ) // Identifier turns a C style type or field name into an exportable Go equivalent. func Identifier(str string) string { prev := rune(-1) return strings.Map(func(r rune) rune { // See https://golang.org/ref/spec#Identifiers switch { case unicode.IsLetter(r): if prev == -1 { r = unicode.ToUpper(r) } case r == '_': switch { // The previous rune was deleted, or we are at the // beginning of the string. case prev == -1: fallthrough // The previous rune is a lower case letter or a digit. case unicode.IsDigit(prev) || (unicode.IsLetter(prev) && unicode.IsLower(prev)): // delete the current rune, and force the // next character to be uppercased. r = -1 } case unicode.IsDigit(r): default: // Delete the current rune. prev is unchanged. return -1 } prev = r return r }, str) } // WriteFormatted outputs a formatted src into out. // // If formatting fails it returns an informative error message. func WriteFormatted(src []byte, out io.Writer) error { formatted, err := format.Source(src) if err == nil { _, err = out.Write(formatted) return err } var el scanner.ErrorList if !errors.As(err, &el) { return err } var nel scanner.ErrorList for _, err := range el { if !err.Pos.IsValid() { nel = append(nel, err) continue } buf := src[err.Pos.Offset:] nl := bytes.IndexRune(buf, '\n') if nl == -1 { nel = append(nel, err) continue } err.Msg += ": " + string(buf[:nl]) nel = append(nel, err) } return nel } // GoTypeName is like %T, but elides the package name. // // Pointers to a type are peeled off. func GoTypeName(t any) string { rT := reflect.TypeOf(t) for rT.Kind() == reflect.Pointer { rT = rT.Elem() } name := rT.Name() if pkgPath := rT.PkgPath(); pkgPath != "" { name = strings.ReplaceAll(name, pkgPath+".", "") } return name } golang-github-cilium-ebpf-0.21.0+ds1/internal/output_test.go000066400000000000000000000017541520243672000237500ustar00rootroot00000000000000package internal import ( "testing" "github.com/go-quicktest/qt" ) func TestIdentifier(t *testing.T) { testcases := []struct { in, out string }{ {".rodata", "Rodata"}, {"_foo_bar_", "FooBar"}, {"ipv6_test", "Ipv6Test"}, {"FOO_BAR", "FOO_BAR"}, {"FOO_", "FOO_"}, {"FOO__BAR", "FOO__BAR"}, {"FOO___BAR", "FOO___BAR"}, {"_FOO__BAR", "FOO__BAR"}, {"__FOO__BAR", "FOO__BAR"}, } for _, tc := range testcases { have := Identifier(tc.in) if have != tc.out { t.Errorf("Expected %q as output of %q, got %q", tc.out, tc.in, have) } } } type foo struct{} func TestGoTypeName(t *testing.T) { type bar[T any] struct{} qt.Assert(t, qt.Equals(GoTypeName(foo{}), "foo")) qt.Assert(t, qt.Equals(GoTypeName(new(foo)), "foo")) qt.Assert(t, qt.Equals(GoTypeName(new(*foo)), "foo")) qt.Assert(t, qt.Equals(GoTypeName(bar[int]{}), "bar[int]")) qt.Assert(t, qt.Equals(GoTypeName(bar[foo]{}), "bar[foo]")) qt.Assert(t, qt.Equals(GoTypeName(bar[testing.T]{}), "bar[testing.T]")) } golang-github-cilium-ebpf-0.21.0+ds1/internal/platform/000077500000000000000000000000001520243672000226375ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/platform/constants.go000066400000000000000000000025351520243672000252070ustar00rootroot00000000000000package platform import "fmt" // Values used to tag platform specific constants. // // The value for Linux is zero so that existing constants do not change. const ( LinuxTag = uint32(iota) << platformShift WindowsTag ) const ( platformMax = 1<<3 - 1 // most not exceed 3 bits to avoid setting the high bit platformShift = 28 platformMask = platformMax << platformShift ) func tagForPlatform(platform string) (uint32, error) { switch platform { case Linux: return LinuxTag, nil case Windows: return WindowsTag, nil default: return 0, fmt.Errorf("unrecognized platform: %s", platform) } } func platformForConstant(c uint32) string { tag := uint32(c & platformMask) switch tag { case LinuxTag: return Linux case WindowsTag: return Windows default: return "" } } // Encode a platform and a value into a tagged constant. // // Returns an error if platform is unknown or c is out of bounds. func EncodeConstant[T ~uint32](platform string, c uint32) (T, error) { if c>>platformShift > 0 { return 0, fmt.Errorf("invalid constant 0x%x", c) } tag, err := tagForPlatform(platform) if err != nil { return 0, err } return T(tag | c), nil } // Decode a platform and a value from a tagged constant. func DecodeConstant[T ~uint32](c T) (string, uint32) { v := uint32(c) & ^uint32(platformMask) return platformForConstant(uint32(c)), v } golang-github-cilium-ebpf-0.21.0+ds1/internal/platform/constants_test.go000066400000000000000000000010711520243672000262400ustar00rootroot00000000000000package platform import ( "testing" "github.com/go-quicktest/qt" ) func TestConstant(t *testing.T) { const maxConstant = uint32(1<= len(set.Val) { return fmt.Errorf("signal %d does not fit within unix.Sigset_t", signal) } // Write the signal bit into its corresponding word at the corrected offset. set.Val[word] |= 1 << (bit % wordBits) return nil } golang-github-cilium-ebpf-0.21.0+ds1/internal/sys/signals_test.go000066400000000000000000000037021520243672000246610ustar00rootroot00000000000000//go:build linux package sys import ( "runtime" "testing" "unsafe" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/unix" ) func TestSigset(t *testing.T) { const maxSignal = unix.Signal(unsafe.Sizeof(unix.Sigset_t{}) * 8) // Type-infer a sigset word. This is a typed uint of 32 or 64 bits depending // on the target architecture, so we can't use an untyped uint. zero := unix.Sigset_t{}.Val[0] words := len(unix.Sigset_t{}.Val) var want, got unix.Sigset_t // Flip the first bit of the first word. if err := sigsetAdd(&got, 1); err != nil { t.Fatal(err) } want.Val[0] = 1 if want != got { t.Fatalf("expected first word to be 0x%x, got: 0x%x", want, got) } // And the last bit of the last word. if err := sigsetAdd(&got, maxSignal); err != nil { t.Fatal(err) } want.Val[words-1] = ^(^zero >> 1) if want != got { t.Fatalf("expected last word to be 0x%x, got: 0x%x", want, got) } if err := sigsetAdd(&got, maxSignal+1); err == nil { t.Fatal("expected out-of-bounds add to be rejected") } if err := sigsetAdd(&got, -1); err == nil { t.Fatal("expected negative signal to be rejected") } } func TestProfilerSignal(t *testing.T) { // Additional goroutine lock to make the PthreadSigmask below execute on the // same OS thread as the functions under test. UnlockOSThread needs to be // called as many times as LockOSThread to unlock the goroutine. runtime.LockOSThread() defer runtime.UnlockOSThread() var old unix.Sigset_t if err := unix.PthreadSigmask(0, nil, &old); err != nil { t.Fatal("get sigmask:", err) } maskProfilerSignal() var have unix.Sigset_t if err := unix.PthreadSigmask(0, nil, &have); err != nil { t.Fatal("get sigmask:", err) } want := have qt.Assert(t, qt.IsNil(sigsetAdd(&want, unix.SIGPROF))) qt.Assert(t, qt.Equals(have, want)) unmaskProfilerSignal() if err := unix.PthreadSigmask(0, nil, &have); err != nil { t.Fatal("get sigmask:", err) } qt.Assert(t, qt.Equals(have, old)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/sys/syscall.go000066400000000000000000000114341520243672000236350ustar00rootroot00000000000000package sys import ( "runtime" "unsafe" "github.com/cilium/ebpf/internal/unix" ) // ENOTSUPP is a Linux internal error code that has leaked into UAPI. // // It is not the same as ENOTSUP or EOPNOTSUPP. const ENOTSUPP = unix.Errno(524) // Info is implemented by all structs that can be passed to the ObjInfo syscall. // // MapInfo // ProgInfo // LinkInfo // BtfInfo type Info interface { info() (unsafe.Pointer, uint32) } var _ Info = (*MapInfo)(nil) func (i *MapInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } var _ Info = (*ProgInfo)(nil) func (i *ProgInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } var _ Info = (*LinkInfo)(nil) func (i *LinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *TracingLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *CgroupLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *NetNsLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *XDPLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *TcxLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *NetfilterLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *NetkitLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *KprobeMultiLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *UprobeMultiLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *RawTracepointLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *KprobeLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *UprobeLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *TracepointLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *EventLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } var _ Info = (*BtfInfo)(nil) func (i *BtfInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } func (i *PerfEventLinkInfo) info() (unsafe.Pointer, uint32) { return unsafe.Pointer(i), uint32(unsafe.Sizeof(*i)) } // ObjInfo retrieves information about a BPF Fd. // // info may be one of MapInfo, ProgInfo, LinkInfo and BtfInfo. func ObjInfo(fd *FD, info Info) error { ptr, len := info.info() err := ObjGetInfoByFd(&ObjGetInfoByFdAttr{ BpfFd: fd.Uint(), InfoLen: len, Info: UnsafePointer(ptr), }) runtime.KeepAlive(fd) return err } // BPFObjName is a null-terminated string made up of // 'A-Za-z0-9_' characters. type ObjName [BPF_OBJ_NAME_LEN]byte // NewObjName truncates the result if it is too long. func NewObjName(name string) ObjName { var result ObjName copy(result[:BPF_OBJ_NAME_LEN-1], name) return result } // LogLevel controls the verbosity of the kernel's eBPF program verifier. type LogLevel uint32 const ( BPF_LOG_LEVEL1 LogLevel = 1 << iota BPF_LOG_LEVEL2 BPF_LOG_STATS ) // MapID uniquely identifies a bpf_map. type MapID uint32 // ProgramID uniquely identifies a bpf_map. type ProgramID uint32 // LinkID uniquely identifies a bpf_link. type LinkID uint32 // BTFID uniquely identifies a BTF blob loaded into the kernel. type BTFID uint32 // TypeID identifies a type in a BTF blob. type TypeID uint32 // Flags used by bpf_mprog. const ( BPF_F_REPLACE = 1 << (iota + 2) BPF_F_BEFORE BPF_F_AFTER BPF_F_ID BPF_F_LINK_MPROG = 1 << 13 // aka BPF_F_LINK ) // Flags used by BPF_PROG_LOAD. const ( BPF_F_SLEEPABLE = 1 << 4 BPF_F_XDP_HAS_FRAGS = 1 << 5 BPF_F_XDP_DEV_BOUND_ONLY = 1 << 6 ) const BPF_TAG_SIZE = 8 const BPF_OBJ_NAME_LEN = 16 // wrappedErrno wraps [unix.Errno] to prevent direct comparisons with // syscall.E* or unix.E* constants. // // You should never export an error of this type. type wrappedErrno struct { unix.Errno } func (we wrappedErrno) Unwrap() error { return we.Errno } func (we wrappedErrno) Error() string { if we.Errno == ENOTSUPP { return "operation not supported" } return we.Errno.Error() } type syscallError struct { error errno unix.Errno } func Error(err error, errno unix.Errno) error { return &syscallError{err, errno} } func (se *syscallError) Is(target error) bool { return target == se.error } func (se *syscallError) Unwrap() error { return se.errno } golang-github-cilium-ebpf-0.21.0+ds1/internal/sys/syscall_other.go000066400000000000000000000036231520243672000250370ustar00rootroot00000000000000//go:build !windows package sys import ( "fmt" "os" "path/filepath" "runtime" "strings" "unsafe" "github.com/cilium/ebpf/internal/unix" ) // BPF wraps SYS_BPF. // // Any pointers contained in attr must use the Pointer type from this package. func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { // Prevent the Go profiler from repeatedly interrupting the verifier, // which could otherwise lead to a livelock due to receiving EAGAIN. if cmd == BPF_PROG_LOAD || cmd == BPF_PROG_RUN { maskProfilerSignal() defer unmaskProfilerSignal() } for { r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) runtime.KeepAlive(attr) // As of ~4.20 the verifier can be interrupted by a signal, // and returns EAGAIN in that case. if errNo == unix.EAGAIN && cmd == BPF_PROG_LOAD { continue } var err error if errNo != 0 { err = wrappedErrno{errNo} } return r1, err } } // ObjGetTyped wraps [ObjGet] with a readlink call to extract the type of the // underlying bpf object. func ObjGetTyped(attr *ObjGetAttr) (*FD, ObjType, error) { fd, err := ObjGet(attr) if err != nil { return nil, 0, err } typ, err := readType(fd) if err != nil { _ = fd.Close() return nil, 0, fmt.Errorf("reading fd type: %w", err) } return fd, typ, nil } // readType returns the bpf object type of the file descriptor by calling // readlink(3). Returns an error if the file descriptor does not represent a bpf // object. func readType(fd *FD) (ObjType, error) { s, err := os.Readlink(filepath.Join("/proc/self/fd/", fd.String())) if err != nil { return 0, fmt.Errorf("readlink fd %d: %w", fd.Int(), err) } s = strings.TrimPrefix(s, "anon_inode:") switch s { case "bpf-map": return BPF_TYPE_MAP, nil case "bpf-prog": return BPF_TYPE_PROG, nil case "bpf-link": return BPF_TYPE_LINK, nil } return 0, fmt.Errorf("unknown type %s of fd %d", s, fd.Int()) } golang-github-cilium-ebpf-0.21.0+ds1/internal/sys/syscall_test.go000066400000000000000000000034611520243672000246750ustar00rootroot00000000000000package sys import ( "errors" "math" "testing" "unsafe" "github.com/cilium/ebpf/internal/testutils/testmain" "github.com/cilium/ebpf/internal/unix" "github.com/go-quicktest/qt" ) func TestBPF(t *testing.T) { fd, err := MapCreate(&MapCreateAttr{ MapType: BPF_MAP_TYPE_HASH, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(fd.Close())) } func TestBPFAllocations(t *testing.T) { n := testing.AllocsPerRun(10, func() { var attr struct { Foo uint64 } BPF(math.MaxUint32, unsafe.Pointer(&attr), 0) }) qt.Assert(t, qt.Equals(n, 0)) } func TestObjName(t *testing.T) { name := NewObjName("more_than_16_characters_long") if name[len(name)-1] != 0 { t.Error("NewBPFObjName doesn't null terminate") } if len(name) != BPF_OBJ_NAME_LEN { t.Errorf("Name is %d instead of %d bytes long", len(name), BPF_OBJ_NAME_LEN) } } func TestWrappedErrno(t *testing.T) { a := error(wrappedErrno{unix.EINVAL}) b := error(unix.EINVAL) if a == b { t.Error("wrappedErrno is comparable to plain errno") } if !errors.Is(a, b) { t.Error("errors.Is(wrappedErrno, errno) returns false") } if errors.Is(a, unix.EAGAIN) { t.Error("errors.Is(wrappedErrno, EAGAIN) returns true") } notsupp := wrappedErrno{ENOTSUPP} qt.Assert(t, qt.StringContains(notsupp.Error(), "operation not supported")) } func TestSyscallError(t *testing.T) { err := errors.New("foo") foo := Error(err, unix.EINVAL) if !errors.Is(foo, unix.EINVAL) { t.Error("SyscallError is not the wrapped errno") } if !errors.Is(foo, err) { t.Error("SyscallError is not the wrapped error") } if errors.Is(unix.EINVAL, foo) { t.Error("Errno is the SyscallError") } if errors.Is(err, foo) { t.Error("Error is the SyscallError") } } func TestMain(m *testing.M) { testmain.Run(m) } golang-github-cilium-ebpf-0.21.0+ds1/internal/sys/syscall_windows.go000066400000000000000000000036001520243672000254030ustar00rootroot00000000000000package sys import ( "fmt" "syscall" "unsafe" "golang.org/x/sys/windows" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/efw" "github.com/cilium/ebpf/internal/unix" ) // BPF calls the BPF syscall wrapper in ebpfapi.dll. // // Any pointers contained in attr must use the Pointer type from this package. // // The implementation lives in https://github.com/microsoft/ebpf-for-windows/blob/main/libs/api/bpf_syscall.cpp func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { // On Linux we need to guard against preemption by the profiler here. On // Windows it seems like a cgocall may not be preempted: // https://github.com/golang/go/blob/8b51146c698bcfcc2c2b73fa9390db5230f2ce0a/src/runtime/os_windows.go#L1240-L1246 addr, err := efw.BPF.Find() if err != nil { return 0, err } // Using [LazyProc.Call] forces attr to escape, which isn't the case when using syscall.Syscall directly. r1, _, lastError := syscall.SyscallN(addr, uintptr(cmd), uintptr(attr), size) if ret := int(efw.Int(r1)); ret < 0 { errNo := unix.Errno(-ret) if errNo == unix.EINVAL && lastError == windows.ERROR_CALL_NOT_IMPLEMENTED { return 0, internal.ErrNotSupportedOnOS } return 0, wrappedErrno{errNo} } return r1, nil } // ObjGetTyped retrieves an pinned object and its type. func ObjGetTyped(attr *ObjGetAttr) (*FD, ObjType, error) { fd, err := ObjGet(attr) if err != nil { return nil, 0, err } efwType, err := efw.EbpfObjectGetInfoByFd(fd.Int(), nil, nil) if err != nil { _ = fd.Close() return nil, 0, err } switch efwType { case efw.EBPF_OBJECT_UNKNOWN: return fd, BPF_TYPE_UNSPEC, nil case efw.EBPF_OBJECT_MAP: return fd, BPF_TYPE_MAP, nil case efw.EBPF_OBJECT_LINK: return fd, BPF_TYPE_LINK, nil case efw.EBPF_OBJECT_PROGRAM: return fd, BPF_TYPE_PROG, nil default: return nil, 0, fmt.Errorf("unrecognized object type %v", efwType) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/sys/types.go000066400000000000000000001565321520243672000233400ustar00rootroot00000000000000// Code generated by internal/cmd/gentypes; DO NOT EDIT. package sys import ( "structs" "unsafe" ) const ( BPF_ADJ_ROOM_ENCAP_L2_MASK = 255 BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56 BPF_ANY = 0 BPF_CSUM_LEVEL_DEC = 2 BPF_CSUM_LEVEL_INC = 1 BPF_CSUM_LEVEL_QUERY = 0 BPF_CSUM_LEVEL_RESET = 3 BPF_EXIST = 2 BPF_FIB_LKUP_RET_BLACKHOLE = 1 BPF_FIB_LKUP_RET_FRAG_NEEDED = 8 BPF_FIB_LKUP_RET_FWD_DISABLED = 5 BPF_FIB_LKUP_RET_NOT_FWDED = 4 BPF_FIB_LKUP_RET_NO_NEIGH = 7 BPF_FIB_LKUP_RET_NO_SRC_ADDR = 9 BPF_FIB_LKUP_RET_PROHIBIT = 3 BPF_FIB_LKUP_RET_SUCCESS = 0 BPF_FIB_LKUP_RET_UNREACHABLE = 2 BPF_FIB_LKUP_RET_UNSUPP_LWT = 6 BPF_FIB_LOOKUP_DIRECT = 1 BPF_FIB_LOOKUP_MARK = 32 BPF_FIB_LOOKUP_OUTPUT = 2 BPF_FIB_LOOKUP_SKIP_NEIGH = 4 BPF_FIB_LOOKUP_SRC = 16 BPF_FIB_LOOKUP_TBID = 8 BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 1 BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 4 BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 2 BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 128 BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 256 BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 64 BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = 2 BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = 4 BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 8 BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 16 BPF_F_ADJ_ROOM_FIXED_GSO = 1 BPF_F_ADJ_ROOM_NO_CSUM_RESET = 32 BPF_F_BPRM_SECUREEXEC = 1 BPF_F_BROADCAST = 8 BPF_F_CLONE = 512 BPF_F_CTXLEN_MASK = 4503595332403200 BPF_F_CURRENT_CPU = 4294967295 BPF_F_CURRENT_NETNS = 18446744073709551615 BPF_F_DONT_FRAGMENT = 4 BPF_F_EXCLUDE_INGRESS = 16 BPF_F_FAST_STACK_CMP = 512 BPF_F_GET_BRANCH_RECORDS_SIZE = 1 BPF_F_HDR_FIELD_MASK = 15 BPF_F_INDEX_MASK = 4294967295 BPF_F_INGRESS = 1 BPF_F_INNER_MAP = 4096 BPF_F_INVALIDATE_HASH = 2 BPF_F_IPV6 = 128 BPF_F_KPROBE_MULTI_RETURN = 1 BPF_F_LINK = 8192 BPF_F_LOCK = 4 BPF_F_MARK_ENFORCE = 64 BPF_F_MARK_MANGLED_0 = 32 BPF_F_MMAPABLE = 1024 BPF_F_NEIGH = 65536 BPF_F_NEXTHOP = 262144 BPF_F_NO_COMMON_LRU = 2 BPF_F_NO_PREALLOC = 1 BPF_F_NO_TUNNEL_KEY = 16 BPF_F_NO_USER_CONV = 262144 BPF_F_NUMA_NODE = 4 BPF_F_PATH_FD = 16384 BPF_F_PEER = 131072 BPF_F_PRESERVE_ELEMS = 2048 BPF_F_PSEUDO_HDR = 16 BPF_F_RDONLY = 8 BPF_F_RDONLY_PROG = 128 BPF_F_RECOMPUTE_CSUM = 1 BPF_F_REUSE_STACKID = 1024 BPF_F_SEGV_ON_FAULT = 131072 BPF_F_SEQ_NUMBER = 8 BPF_F_SKIP_FIELD_MASK = 255 BPF_F_STACK_BUILD_ID = 32 BPF_F_SYSCTL_BASE_NAME = 1 BPF_F_TIMER_ABS = 1 BPF_F_TIMER_CPU_PIN = 2 BPF_F_TOKEN_FD = 65536 BPF_F_TUNINFO_FLAGS = 16 BPF_F_TUNINFO_IPV6 = 1 BPF_F_UPROBE_MULTI_RETURN = 1 BPF_F_USER_BUILD_ID = 2048 BPF_F_USER_STACK = 256 BPF_F_VTYPE_BTF_OBJ_FD = 32768 BPF_F_WRONLY = 16 BPF_F_WRONLY_PROG = 256 BPF_F_ZERO_CSUM_TX = 2 BPF_F_ZERO_SEED = 64 BPF_LOAD_HDR_OPT_TCP_SYN = 1 BPF_LOCAL_STORAGE_GET_F_CREATE = 1 BPF_MAX_LOOPS = 8388608 BPF_MAX_TIMED_LOOPS = 65535 BPF_MAX_TRAMP_LINKS = 38 BPF_NOEXIST = 1 BPF_RB_AVAIL_DATA = 0 BPF_RB_CONS_POS = 2 BPF_RB_FORCE_WAKEUP = 2 BPF_RB_NO_WAKEUP = 1 BPF_RB_PROD_POS = 3 BPF_RB_RING_SIZE = 1 BPF_REG_0 = 0 BPF_REG_1 = 1 BPF_REG_10 = 10 BPF_REG_2 = 2 BPF_REG_3 = 3 BPF_REG_4 = 4 BPF_REG_5 = 5 BPF_REG_6 = 6 BPF_REG_7 = 7 BPF_REG_8 = 8 BPF_REG_9 = 9 BPF_RINGBUF_BUSY_BIT = 2147483648 BPF_RINGBUF_DISCARD_BIT = 1073741824 BPF_RINGBUF_HDR_SZ = 8 BPF_SKB_CLOCK_MONOTONIC = 1 BPF_SKB_CLOCK_REALTIME = 0 BPF_SKB_CLOCK_TAI = 2 BPF_SKB_TSTAMP_DELIVERY_MONO = 1 BPF_SKB_TSTAMP_UNSPEC = 0 BPF_SK_LOOKUP_F_NO_REUSEPORT = 2 BPF_SK_LOOKUP_F_REPLACE = 1 BPF_SK_STORAGE_GET_F_CREATE = 1 BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB = 4 BPF_SOCK_OPS_ALL_CB_FLAGS = 127 BPF_SOCK_OPS_BASE_RTT = 7 BPF_SOCK_OPS_HDR_OPT_LEN_CB = 14 BPF_SOCK_OPS_NEEDS_ECN = 6 BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG = 16 BPF_SOCK_OPS_PARSE_HDR_OPT_CB = 13 BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = 32 BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB = 5 BPF_SOCK_OPS_RETRANS_CB = 9 BPF_SOCK_OPS_RETRANS_CB_FLAG = 2 BPF_SOCK_OPS_RTO_CB = 8 BPF_SOCK_OPS_RTO_CB_FLAG = 1 BPF_SOCK_OPS_RTT_CB = 12 BPF_SOCK_OPS_RTT_CB_FLAG = 8 BPF_SOCK_OPS_RWND_INIT = 2 BPF_SOCK_OPS_STATE_CB = 10 BPF_SOCK_OPS_STATE_CB_FLAG = 4 BPF_SOCK_OPS_TCP_CONNECT_CB = 3 BPF_SOCK_OPS_TCP_LISTEN_CB = 11 BPF_SOCK_OPS_TIMEOUT_INIT = 1 BPF_SOCK_OPS_TSTAMP_ACK_CB = 19 BPF_SOCK_OPS_TSTAMP_SCHED_CB = 16 BPF_SOCK_OPS_TSTAMP_SENDMSG_CB = 20 BPF_SOCK_OPS_TSTAMP_SND_HW_CB = 18 BPF_SOCK_OPS_TSTAMP_SND_SW_CB = 17 BPF_SOCK_OPS_VOID = 0 BPF_SOCK_OPS_WRITE_HDR_OPT_CB = 15 BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG = 64 BPF_STREAM_MAX_CAPACITY = 100000 BPF_TASK_ITER_ALL_PROCS = 0 BPF_TASK_ITER_ALL_THREADS = 1 BPF_TASK_ITER_PROC_THREADS = 2 BPF_TCP_BOUND_INACTIVE = 13 BPF_TCP_CLOSE = 7 BPF_TCP_CLOSE_WAIT = 8 BPF_TCP_CLOSING = 11 BPF_TCP_ESTABLISHED = 1 BPF_TCP_FIN_WAIT1 = 4 BPF_TCP_FIN_WAIT2 = 5 BPF_TCP_LAST_ACK = 9 BPF_TCP_LISTEN = 10 BPF_TCP_MAX_STATES = 14 BPF_TCP_NEW_SYN_RECV = 12 BPF_TCP_SYN_RECV = 3 BPF_TCP_SYN_SENT = 2 BPF_TCP_TIME_WAIT = 6 BPF_WRITE_HDR_TCP_CURRENT_MSS = 1 BPF_WRITE_HDR_TCP_SYNACK_COOKIE = 2 BPF_XFRM_STATE_OPTS_SZ = 36 ) type AdjRoomMode uint32 const ( BPF_ADJ_ROOM_NET AdjRoomMode = 0 BPF_ADJ_ROOM_MAC AdjRoomMode = 1 ) type AttachType uint32 const ( BPF_CGROUP_INET_INGRESS AttachType = 0 BPF_CGROUP_INET_EGRESS AttachType = 1 BPF_CGROUP_INET_SOCK_CREATE AttachType = 2 BPF_CGROUP_SOCK_OPS AttachType = 3 BPF_SK_SKB_STREAM_PARSER AttachType = 4 BPF_SK_SKB_STREAM_VERDICT AttachType = 5 BPF_CGROUP_DEVICE AttachType = 6 BPF_SK_MSG_VERDICT AttachType = 7 BPF_CGROUP_INET4_BIND AttachType = 8 BPF_CGROUP_INET6_BIND AttachType = 9 BPF_CGROUP_INET4_CONNECT AttachType = 10 BPF_CGROUP_INET6_CONNECT AttachType = 11 BPF_CGROUP_INET4_POST_BIND AttachType = 12 BPF_CGROUP_INET6_POST_BIND AttachType = 13 BPF_CGROUP_UDP4_SENDMSG AttachType = 14 BPF_CGROUP_UDP6_SENDMSG AttachType = 15 BPF_LIRC_MODE2 AttachType = 16 BPF_FLOW_DISSECTOR AttachType = 17 BPF_CGROUP_SYSCTL AttachType = 18 BPF_CGROUP_UDP4_RECVMSG AttachType = 19 BPF_CGROUP_UDP6_RECVMSG AttachType = 20 BPF_CGROUP_GETSOCKOPT AttachType = 21 BPF_CGROUP_SETSOCKOPT AttachType = 22 BPF_TRACE_RAW_TP AttachType = 23 BPF_TRACE_FENTRY AttachType = 24 BPF_TRACE_FEXIT AttachType = 25 BPF_MODIFY_RETURN AttachType = 26 BPF_LSM_MAC AttachType = 27 BPF_TRACE_ITER AttachType = 28 BPF_CGROUP_INET4_GETPEERNAME AttachType = 29 BPF_CGROUP_INET6_GETPEERNAME AttachType = 30 BPF_CGROUP_INET4_GETSOCKNAME AttachType = 31 BPF_CGROUP_INET6_GETSOCKNAME AttachType = 32 BPF_XDP_DEVMAP AttachType = 33 BPF_CGROUP_INET_SOCK_RELEASE AttachType = 34 BPF_XDP_CPUMAP AttachType = 35 BPF_SK_LOOKUP AttachType = 36 BPF_XDP AttachType = 37 BPF_SK_SKB_VERDICT AttachType = 38 BPF_SK_REUSEPORT_SELECT AttachType = 39 BPF_SK_REUSEPORT_SELECT_OR_MIGRATE AttachType = 40 BPF_PERF_EVENT AttachType = 41 BPF_TRACE_KPROBE_MULTI AttachType = 42 BPF_LSM_CGROUP AttachType = 43 BPF_STRUCT_OPS AttachType = 44 BPF_NETFILTER AttachType = 45 BPF_TCX_INGRESS AttachType = 46 BPF_TCX_EGRESS AttachType = 47 BPF_TRACE_UPROBE_MULTI AttachType = 48 BPF_CGROUP_UNIX_CONNECT AttachType = 49 BPF_CGROUP_UNIX_SENDMSG AttachType = 50 BPF_CGROUP_UNIX_RECVMSG AttachType = 51 BPF_CGROUP_UNIX_GETPEERNAME AttachType = 52 BPF_CGROUP_UNIX_GETSOCKNAME AttachType = 53 BPF_NETKIT_PRIMARY AttachType = 54 BPF_NETKIT_PEER AttachType = 55 BPF_TRACE_KPROBE_SESSION AttachType = 56 BPF_TRACE_UPROBE_SESSION AttachType = 57 __MAX_BPF_ATTACH_TYPE AttachType = 58 ) type Cmd uint32 const ( BPF_MAP_CREATE Cmd = 0 BPF_MAP_LOOKUP_ELEM Cmd = 1 BPF_MAP_UPDATE_ELEM Cmd = 2 BPF_MAP_DELETE_ELEM Cmd = 3 BPF_MAP_GET_NEXT_KEY Cmd = 4 BPF_PROG_LOAD Cmd = 5 BPF_OBJ_PIN Cmd = 6 BPF_OBJ_GET Cmd = 7 BPF_PROG_ATTACH Cmd = 8 BPF_PROG_DETACH Cmd = 9 BPF_PROG_TEST_RUN Cmd = 10 BPF_PROG_RUN Cmd = 10 BPF_PROG_GET_NEXT_ID Cmd = 11 BPF_MAP_GET_NEXT_ID Cmd = 12 BPF_PROG_GET_FD_BY_ID Cmd = 13 BPF_MAP_GET_FD_BY_ID Cmd = 14 BPF_OBJ_GET_INFO_BY_FD Cmd = 15 BPF_PROG_QUERY Cmd = 16 BPF_RAW_TRACEPOINT_OPEN Cmd = 17 BPF_BTF_LOAD Cmd = 18 BPF_BTF_GET_FD_BY_ID Cmd = 19 BPF_TASK_FD_QUERY Cmd = 20 BPF_MAP_LOOKUP_AND_DELETE_ELEM Cmd = 21 BPF_MAP_FREEZE Cmd = 22 BPF_BTF_GET_NEXT_ID Cmd = 23 BPF_MAP_LOOKUP_BATCH Cmd = 24 BPF_MAP_LOOKUP_AND_DELETE_BATCH Cmd = 25 BPF_MAP_UPDATE_BATCH Cmd = 26 BPF_MAP_DELETE_BATCH Cmd = 27 BPF_LINK_CREATE Cmd = 28 BPF_LINK_UPDATE Cmd = 29 BPF_LINK_GET_FD_BY_ID Cmd = 30 BPF_LINK_GET_NEXT_ID Cmd = 31 BPF_ENABLE_STATS Cmd = 32 BPF_ITER_CREATE Cmd = 33 BPF_LINK_DETACH Cmd = 34 BPF_PROG_BIND_MAP Cmd = 35 BPF_TOKEN_CREATE Cmd = 36 BPF_PROG_STREAM_READ_BY_FD Cmd = 37 __MAX_BPF_CMD Cmd = 38 ) type FunctionId uint32 const ( BPF_FUNC_unspec FunctionId = 0 BPF_FUNC_map_lookup_elem FunctionId = 1 BPF_FUNC_map_update_elem FunctionId = 2 BPF_FUNC_map_delete_elem FunctionId = 3 BPF_FUNC_probe_read FunctionId = 4 BPF_FUNC_ktime_get_ns FunctionId = 5 BPF_FUNC_trace_printk FunctionId = 6 BPF_FUNC_get_prandom_u32 FunctionId = 7 BPF_FUNC_get_smp_processor_id FunctionId = 8 BPF_FUNC_skb_store_bytes FunctionId = 9 BPF_FUNC_l3_csum_replace FunctionId = 10 BPF_FUNC_l4_csum_replace FunctionId = 11 BPF_FUNC_tail_call FunctionId = 12 BPF_FUNC_clone_redirect FunctionId = 13 BPF_FUNC_get_current_pid_tgid FunctionId = 14 BPF_FUNC_get_current_uid_gid FunctionId = 15 BPF_FUNC_get_current_comm FunctionId = 16 BPF_FUNC_get_cgroup_classid FunctionId = 17 BPF_FUNC_skb_vlan_push FunctionId = 18 BPF_FUNC_skb_vlan_pop FunctionId = 19 BPF_FUNC_skb_get_tunnel_key FunctionId = 20 BPF_FUNC_skb_set_tunnel_key FunctionId = 21 BPF_FUNC_perf_event_read FunctionId = 22 BPF_FUNC_redirect FunctionId = 23 BPF_FUNC_get_route_realm FunctionId = 24 BPF_FUNC_perf_event_output FunctionId = 25 BPF_FUNC_skb_load_bytes FunctionId = 26 BPF_FUNC_get_stackid FunctionId = 27 BPF_FUNC_csum_diff FunctionId = 28 BPF_FUNC_skb_get_tunnel_opt FunctionId = 29 BPF_FUNC_skb_set_tunnel_opt FunctionId = 30 BPF_FUNC_skb_change_proto FunctionId = 31 BPF_FUNC_skb_change_type FunctionId = 32 BPF_FUNC_skb_under_cgroup FunctionId = 33 BPF_FUNC_get_hash_recalc FunctionId = 34 BPF_FUNC_get_current_task FunctionId = 35 BPF_FUNC_probe_write_user FunctionId = 36 BPF_FUNC_current_task_under_cgroup FunctionId = 37 BPF_FUNC_skb_change_tail FunctionId = 38 BPF_FUNC_skb_pull_data FunctionId = 39 BPF_FUNC_csum_update FunctionId = 40 BPF_FUNC_set_hash_invalid FunctionId = 41 BPF_FUNC_get_numa_node_id FunctionId = 42 BPF_FUNC_skb_change_head FunctionId = 43 BPF_FUNC_xdp_adjust_head FunctionId = 44 BPF_FUNC_probe_read_str FunctionId = 45 BPF_FUNC_get_socket_cookie FunctionId = 46 BPF_FUNC_get_socket_uid FunctionId = 47 BPF_FUNC_set_hash FunctionId = 48 BPF_FUNC_setsockopt FunctionId = 49 BPF_FUNC_skb_adjust_room FunctionId = 50 BPF_FUNC_redirect_map FunctionId = 51 BPF_FUNC_sk_redirect_map FunctionId = 52 BPF_FUNC_sock_map_update FunctionId = 53 BPF_FUNC_xdp_adjust_meta FunctionId = 54 BPF_FUNC_perf_event_read_value FunctionId = 55 BPF_FUNC_perf_prog_read_value FunctionId = 56 BPF_FUNC_getsockopt FunctionId = 57 BPF_FUNC_override_return FunctionId = 58 BPF_FUNC_sock_ops_cb_flags_set FunctionId = 59 BPF_FUNC_msg_redirect_map FunctionId = 60 BPF_FUNC_msg_apply_bytes FunctionId = 61 BPF_FUNC_msg_cork_bytes FunctionId = 62 BPF_FUNC_msg_pull_data FunctionId = 63 BPF_FUNC_bind FunctionId = 64 BPF_FUNC_xdp_adjust_tail FunctionId = 65 BPF_FUNC_skb_get_xfrm_state FunctionId = 66 BPF_FUNC_get_stack FunctionId = 67 BPF_FUNC_skb_load_bytes_relative FunctionId = 68 BPF_FUNC_fib_lookup FunctionId = 69 BPF_FUNC_sock_hash_update FunctionId = 70 BPF_FUNC_msg_redirect_hash FunctionId = 71 BPF_FUNC_sk_redirect_hash FunctionId = 72 BPF_FUNC_lwt_push_encap FunctionId = 73 BPF_FUNC_lwt_seg6_store_bytes FunctionId = 74 BPF_FUNC_lwt_seg6_adjust_srh FunctionId = 75 BPF_FUNC_lwt_seg6_action FunctionId = 76 BPF_FUNC_rc_repeat FunctionId = 77 BPF_FUNC_rc_keydown FunctionId = 78 BPF_FUNC_skb_cgroup_id FunctionId = 79 BPF_FUNC_get_current_cgroup_id FunctionId = 80 BPF_FUNC_get_local_storage FunctionId = 81 BPF_FUNC_sk_select_reuseport FunctionId = 82 BPF_FUNC_skb_ancestor_cgroup_id FunctionId = 83 BPF_FUNC_sk_lookup_tcp FunctionId = 84 BPF_FUNC_sk_lookup_udp FunctionId = 85 BPF_FUNC_sk_release FunctionId = 86 BPF_FUNC_map_push_elem FunctionId = 87 BPF_FUNC_map_pop_elem FunctionId = 88 BPF_FUNC_map_peek_elem FunctionId = 89 BPF_FUNC_msg_push_data FunctionId = 90 BPF_FUNC_msg_pop_data FunctionId = 91 BPF_FUNC_rc_pointer_rel FunctionId = 92 BPF_FUNC_spin_lock FunctionId = 93 BPF_FUNC_spin_unlock FunctionId = 94 BPF_FUNC_sk_fullsock FunctionId = 95 BPF_FUNC_tcp_sock FunctionId = 96 BPF_FUNC_skb_ecn_set_ce FunctionId = 97 BPF_FUNC_get_listener_sock FunctionId = 98 BPF_FUNC_skc_lookup_tcp FunctionId = 99 BPF_FUNC_tcp_check_syncookie FunctionId = 100 BPF_FUNC_sysctl_get_name FunctionId = 101 BPF_FUNC_sysctl_get_current_value FunctionId = 102 BPF_FUNC_sysctl_get_new_value FunctionId = 103 BPF_FUNC_sysctl_set_new_value FunctionId = 104 BPF_FUNC_strtol FunctionId = 105 BPF_FUNC_strtoul FunctionId = 106 BPF_FUNC_sk_storage_get FunctionId = 107 BPF_FUNC_sk_storage_delete FunctionId = 108 BPF_FUNC_send_signal FunctionId = 109 BPF_FUNC_tcp_gen_syncookie FunctionId = 110 BPF_FUNC_skb_output FunctionId = 111 BPF_FUNC_probe_read_user FunctionId = 112 BPF_FUNC_probe_read_kernel FunctionId = 113 BPF_FUNC_probe_read_user_str FunctionId = 114 BPF_FUNC_probe_read_kernel_str FunctionId = 115 BPF_FUNC_tcp_send_ack FunctionId = 116 BPF_FUNC_send_signal_thread FunctionId = 117 BPF_FUNC_jiffies64 FunctionId = 118 BPF_FUNC_read_branch_records FunctionId = 119 BPF_FUNC_get_ns_current_pid_tgid FunctionId = 120 BPF_FUNC_xdp_output FunctionId = 121 BPF_FUNC_get_netns_cookie FunctionId = 122 BPF_FUNC_get_current_ancestor_cgroup_id FunctionId = 123 BPF_FUNC_sk_assign FunctionId = 124 BPF_FUNC_ktime_get_boot_ns FunctionId = 125 BPF_FUNC_seq_printf FunctionId = 126 BPF_FUNC_seq_write FunctionId = 127 BPF_FUNC_sk_cgroup_id FunctionId = 128 BPF_FUNC_sk_ancestor_cgroup_id FunctionId = 129 BPF_FUNC_ringbuf_output FunctionId = 130 BPF_FUNC_ringbuf_reserve FunctionId = 131 BPF_FUNC_ringbuf_submit FunctionId = 132 BPF_FUNC_ringbuf_discard FunctionId = 133 BPF_FUNC_ringbuf_query FunctionId = 134 BPF_FUNC_csum_level FunctionId = 135 BPF_FUNC_skc_to_tcp6_sock FunctionId = 136 BPF_FUNC_skc_to_tcp_sock FunctionId = 137 BPF_FUNC_skc_to_tcp_timewait_sock FunctionId = 138 BPF_FUNC_skc_to_tcp_request_sock FunctionId = 139 BPF_FUNC_skc_to_udp6_sock FunctionId = 140 BPF_FUNC_get_task_stack FunctionId = 141 BPF_FUNC_load_hdr_opt FunctionId = 142 BPF_FUNC_store_hdr_opt FunctionId = 143 BPF_FUNC_reserve_hdr_opt FunctionId = 144 BPF_FUNC_inode_storage_get FunctionId = 145 BPF_FUNC_inode_storage_delete FunctionId = 146 BPF_FUNC_d_path FunctionId = 147 BPF_FUNC_copy_from_user FunctionId = 148 BPF_FUNC_snprintf_btf FunctionId = 149 BPF_FUNC_seq_printf_btf FunctionId = 150 BPF_FUNC_skb_cgroup_classid FunctionId = 151 BPF_FUNC_redirect_neigh FunctionId = 152 BPF_FUNC_per_cpu_ptr FunctionId = 153 BPF_FUNC_this_cpu_ptr FunctionId = 154 BPF_FUNC_redirect_peer FunctionId = 155 BPF_FUNC_task_storage_get FunctionId = 156 BPF_FUNC_task_storage_delete FunctionId = 157 BPF_FUNC_get_current_task_btf FunctionId = 158 BPF_FUNC_bprm_opts_set FunctionId = 159 BPF_FUNC_ktime_get_coarse_ns FunctionId = 160 BPF_FUNC_ima_inode_hash FunctionId = 161 BPF_FUNC_sock_from_file FunctionId = 162 BPF_FUNC_check_mtu FunctionId = 163 BPF_FUNC_for_each_map_elem FunctionId = 164 BPF_FUNC_snprintf FunctionId = 165 BPF_FUNC_sys_bpf FunctionId = 166 BPF_FUNC_btf_find_by_name_kind FunctionId = 167 BPF_FUNC_sys_close FunctionId = 168 BPF_FUNC_timer_init FunctionId = 169 BPF_FUNC_timer_set_callback FunctionId = 170 BPF_FUNC_timer_start FunctionId = 171 BPF_FUNC_timer_cancel FunctionId = 172 BPF_FUNC_get_func_ip FunctionId = 173 BPF_FUNC_get_attach_cookie FunctionId = 174 BPF_FUNC_task_pt_regs FunctionId = 175 BPF_FUNC_get_branch_snapshot FunctionId = 176 BPF_FUNC_trace_vprintk FunctionId = 177 BPF_FUNC_skc_to_unix_sock FunctionId = 178 BPF_FUNC_kallsyms_lookup_name FunctionId = 179 BPF_FUNC_find_vma FunctionId = 180 BPF_FUNC_loop FunctionId = 181 BPF_FUNC_strncmp FunctionId = 182 BPF_FUNC_get_func_arg FunctionId = 183 BPF_FUNC_get_func_ret FunctionId = 184 BPF_FUNC_get_func_arg_cnt FunctionId = 185 BPF_FUNC_get_retval FunctionId = 186 BPF_FUNC_set_retval FunctionId = 187 BPF_FUNC_xdp_get_buff_len FunctionId = 188 BPF_FUNC_xdp_load_bytes FunctionId = 189 BPF_FUNC_xdp_store_bytes FunctionId = 190 BPF_FUNC_copy_from_user_task FunctionId = 191 BPF_FUNC_skb_set_tstamp FunctionId = 192 BPF_FUNC_ima_file_hash FunctionId = 193 BPF_FUNC_kptr_xchg FunctionId = 194 BPF_FUNC_map_lookup_percpu_elem FunctionId = 195 BPF_FUNC_skc_to_mptcp_sock FunctionId = 196 BPF_FUNC_dynptr_from_mem FunctionId = 197 BPF_FUNC_ringbuf_reserve_dynptr FunctionId = 198 BPF_FUNC_ringbuf_submit_dynptr FunctionId = 199 BPF_FUNC_ringbuf_discard_dynptr FunctionId = 200 BPF_FUNC_dynptr_read FunctionId = 201 BPF_FUNC_dynptr_write FunctionId = 202 BPF_FUNC_dynptr_data FunctionId = 203 BPF_FUNC_tcp_raw_gen_syncookie_ipv4 FunctionId = 204 BPF_FUNC_tcp_raw_gen_syncookie_ipv6 FunctionId = 205 BPF_FUNC_tcp_raw_check_syncookie_ipv4 FunctionId = 206 BPF_FUNC_tcp_raw_check_syncookie_ipv6 FunctionId = 207 BPF_FUNC_ktime_get_tai_ns FunctionId = 208 BPF_FUNC_user_ringbuf_drain FunctionId = 209 BPF_FUNC_cgrp_storage_get FunctionId = 210 BPF_FUNC_cgrp_storage_delete FunctionId = 211 __BPF_FUNC_MAX_ID FunctionId = 212 ) type HdrStartOff uint32 const ( BPF_HDR_START_MAC HdrStartOff = 0 BPF_HDR_START_NET HdrStartOff = 1 ) type LinkType uint32 const ( BPF_LINK_TYPE_UNSPEC LinkType = 0 BPF_LINK_TYPE_RAW_TRACEPOINT LinkType = 1 BPF_LINK_TYPE_TRACING LinkType = 2 BPF_LINK_TYPE_CGROUP LinkType = 3 BPF_LINK_TYPE_ITER LinkType = 4 BPF_LINK_TYPE_NETNS LinkType = 5 BPF_LINK_TYPE_XDP LinkType = 6 BPF_LINK_TYPE_PERF_EVENT LinkType = 7 BPF_LINK_TYPE_KPROBE_MULTI LinkType = 8 BPF_LINK_TYPE_STRUCT_OPS LinkType = 9 BPF_LINK_TYPE_NETFILTER LinkType = 10 BPF_LINK_TYPE_TCX LinkType = 11 BPF_LINK_TYPE_UPROBE_MULTI LinkType = 12 BPF_LINK_TYPE_NETKIT LinkType = 13 BPF_LINK_TYPE_SOCKMAP LinkType = 14 __MAX_BPF_LINK_TYPE LinkType = 15 ) type MapType uint32 const ( BPF_MAP_TYPE_UNSPEC MapType = 0 BPF_MAP_TYPE_HASH MapType = 1 BPF_MAP_TYPE_ARRAY MapType = 2 BPF_MAP_TYPE_PROG_ARRAY MapType = 3 BPF_MAP_TYPE_PERF_EVENT_ARRAY MapType = 4 BPF_MAP_TYPE_PERCPU_HASH MapType = 5 BPF_MAP_TYPE_PERCPU_ARRAY MapType = 6 BPF_MAP_TYPE_STACK_TRACE MapType = 7 BPF_MAP_TYPE_CGROUP_ARRAY MapType = 8 BPF_MAP_TYPE_LRU_HASH MapType = 9 BPF_MAP_TYPE_LRU_PERCPU_HASH MapType = 10 BPF_MAP_TYPE_LPM_TRIE MapType = 11 BPF_MAP_TYPE_ARRAY_OF_MAPS MapType = 12 BPF_MAP_TYPE_HASH_OF_MAPS MapType = 13 BPF_MAP_TYPE_DEVMAP MapType = 14 BPF_MAP_TYPE_SOCKMAP MapType = 15 BPF_MAP_TYPE_CPUMAP MapType = 16 BPF_MAP_TYPE_XSKMAP MapType = 17 BPF_MAP_TYPE_SOCKHASH MapType = 18 BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED MapType = 19 BPF_MAP_TYPE_CGROUP_STORAGE MapType = 19 BPF_MAP_TYPE_REUSEPORT_SOCKARRAY MapType = 20 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED MapType = 21 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE MapType = 21 BPF_MAP_TYPE_QUEUE MapType = 22 BPF_MAP_TYPE_STACK MapType = 23 BPF_MAP_TYPE_SK_STORAGE MapType = 24 BPF_MAP_TYPE_DEVMAP_HASH MapType = 25 BPF_MAP_TYPE_STRUCT_OPS MapType = 26 BPF_MAP_TYPE_RINGBUF MapType = 27 BPF_MAP_TYPE_INODE_STORAGE MapType = 28 BPF_MAP_TYPE_TASK_STORAGE MapType = 29 BPF_MAP_TYPE_BLOOM_FILTER MapType = 30 BPF_MAP_TYPE_USER_RINGBUF MapType = 31 BPF_MAP_TYPE_CGRP_STORAGE MapType = 32 BPF_MAP_TYPE_ARENA MapType = 33 __MAX_BPF_MAP_TYPE MapType = 34 ) type NetfilterInetHook uint32 const ( NF_INET_PRE_ROUTING NetfilterInetHook = 0 NF_INET_LOCAL_IN NetfilterInetHook = 1 NF_INET_FORWARD NetfilterInetHook = 2 NF_INET_LOCAL_OUT NetfilterInetHook = 3 NF_INET_POST_ROUTING NetfilterInetHook = 4 NF_INET_NUMHOOKS NetfilterInetHook = 5 NF_INET_INGRESS NetfilterInetHook = 5 ) type ObjType uint32 const ( BPF_TYPE_UNSPEC ObjType = 0 BPF_TYPE_PROG ObjType = 1 BPF_TYPE_MAP ObjType = 2 BPF_TYPE_LINK ObjType = 3 ) type PerfEventType uint32 const ( BPF_PERF_EVENT_UNSPEC PerfEventType = 0 BPF_PERF_EVENT_UPROBE PerfEventType = 1 BPF_PERF_EVENT_URETPROBE PerfEventType = 2 BPF_PERF_EVENT_KPROBE PerfEventType = 3 BPF_PERF_EVENT_KRETPROBE PerfEventType = 4 BPF_PERF_EVENT_TRACEPOINT PerfEventType = 5 BPF_PERF_EVENT_EVENT PerfEventType = 6 ) type ProgType uint32 const ( BPF_PROG_TYPE_UNSPEC ProgType = 0 BPF_PROG_TYPE_SOCKET_FILTER ProgType = 1 BPF_PROG_TYPE_KPROBE ProgType = 2 BPF_PROG_TYPE_SCHED_CLS ProgType = 3 BPF_PROG_TYPE_SCHED_ACT ProgType = 4 BPF_PROG_TYPE_TRACEPOINT ProgType = 5 BPF_PROG_TYPE_XDP ProgType = 6 BPF_PROG_TYPE_PERF_EVENT ProgType = 7 BPF_PROG_TYPE_CGROUP_SKB ProgType = 8 BPF_PROG_TYPE_CGROUP_SOCK ProgType = 9 BPF_PROG_TYPE_LWT_IN ProgType = 10 BPF_PROG_TYPE_LWT_OUT ProgType = 11 BPF_PROG_TYPE_LWT_XMIT ProgType = 12 BPF_PROG_TYPE_SOCK_OPS ProgType = 13 BPF_PROG_TYPE_SK_SKB ProgType = 14 BPF_PROG_TYPE_CGROUP_DEVICE ProgType = 15 BPF_PROG_TYPE_SK_MSG ProgType = 16 BPF_PROG_TYPE_RAW_TRACEPOINT ProgType = 17 BPF_PROG_TYPE_CGROUP_SOCK_ADDR ProgType = 18 BPF_PROG_TYPE_LWT_SEG6LOCAL ProgType = 19 BPF_PROG_TYPE_LIRC_MODE2 ProgType = 20 BPF_PROG_TYPE_SK_REUSEPORT ProgType = 21 BPF_PROG_TYPE_FLOW_DISSECTOR ProgType = 22 BPF_PROG_TYPE_CGROUP_SYSCTL ProgType = 23 BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE ProgType = 24 BPF_PROG_TYPE_CGROUP_SOCKOPT ProgType = 25 BPF_PROG_TYPE_TRACING ProgType = 26 BPF_PROG_TYPE_STRUCT_OPS ProgType = 27 BPF_PROG_TYPE_EXT ProgType = 28 BPF_PROG_TYPE_LSM ProgType = 29 BPF_PROG_TYPE_SK_LOOKUP ProgType = 30 BPF_PROG_TYPE_SYSCALL ProgType = 31 BPF_PROG_TYPE_NETFILTER ProgType = 32 __MAX_BPF_PROG_TYPE ProgType = 33 ) type RetCode uint32 const ( BPF_OK RetCode = 0 BPF_DROP RetCode = 2 BPF_REDIRECT RetCode = 7 BPF_LWT_REROUTE RetCode = 128 BPF_FLOW_DISSECTOR_CONTINUE RetCode = 129 ) type SkAction uint32 const ( SK_DROP SkAction = 0 SK_PASS SkAction = 1 ) type StackBuildIdStatus uint32 const ( BPF_STACK_BUILD_ID_EMPTY StackBuildIdStatus = 0 BPF_STACK_BUILD_ID_VALID StackBuildIdStatus = 1 BPF_STACK_BUILD_ID_IP StackBuildIdStatus = 2 ) type StatsType uint32 const ( BPF_STATS_RUN_TIME StatsType = 0 ) type TcxActionBase int32 const ( TCX_NEXT TcxActionBase = -1 TCX_PASS TcxActionBase = 0 TCX_DROP TcxActionBase = 2 TCX_REDIRECT TcxActionBase = 7 ) type XdpAction uint32 const ( XDP_ABORTED XdpAction = 0 XDP_DROP XdpAction = 1 XDP_PASS XdpAction = 2 XDP_TX XdpAction = 3 XDP_REDIRECT XdpAction = 4 ) type NetfilterProtocolFamily uint32 const ( NFPROTO_UNSPEC NetfilterProtocolFamily = 0 NFPROTO_INET NetfilterProtocolFamily = 1 NFPROTO_IPV4 NetfilterProtocolFamily = 2 NFPROTO_ARP NetfilterProtocolFamily = 3 NFPROTO_NETDEV NetfilterProtocolFamily = 5 NFPROTO_BRIDGE NetfilterProtocolFamily = 7 NFPROTO_IPV6 NetfilterProtocolFamily = 10 NFPROTO_NUMPROTO NetfilterProtocolFamily = 11 ) type BtfInfo struct { _ structs.HostLayout Btf TypedPointer[uint8] BtfSize uint32 Id BTFID Name TypedPointer[uint8] NameLen uint32 KernelBtf uint32 } type FuncInfo struct { _ structs.HostLayout InsnOff uint32 TypeId uint32 } type LineInfo struct { _ structs.HostLayout InsnOff uint32 FileNameOff uint32 LineOff uint32 LineCol uint32 } type LinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte Extra [48]uint8 } type MapInfo struct { _ structs.HostLayout Type uint32 Id MapID KeySize uint32 ValueSize uint32 MaxEntries uint32 MapFlags uint32 Name ObjName Ifindex uint32 BtfVmlinuxValueTypeId TypeID NetnsDev uint64 NetnsIno uint64 BtfId uint32 BtfKeyTypeId TypeID BtfValueTypeId TypeID BtfVmlinuxId uint32 MapExtra uint64 Hash uint64 HashSize uint32 _ [4]byte } type ProgInfo struct { _ structs.HostLayout Type uint32 Id uint32 Tag [8]uint8 JitedProgLen uint32 XlatedProgLen uint32 JitedProgInsns TypedPointer[uint8] XlatedProgInsns TypedPointer[uint8] LoadTime uint64 CreatedByUid uint32 NrMapIds uint32 MapIds TypedPointer[MapID] Name ObjName Ifindex uint32 _ [4]byte /* unsupported bitfield */ NetnsDev uint64 NetnsIno uint64 NrJitedKsyms uint32 NrJitedFuncLens uint32 JitedKsyms TypedPointer[uint64] JitedFuncLens TypedPointer[uint32] BtfId BTFID FuncInfoRecSize uint32 FuncInfo TypedPointer[uint8] NrFuncInfo uint32 NrLineInfo uint32 LineInfo TypedPointer[uint8] JitedLineInfo TypedPointer[uint64] NrJitedLineInfo uint32 LineInfoRecSize uint32 JitedLineInfoRecSize uint32 NrProgTags uint32 ProgTags uint64 RunTimeNs uint64 RunCnt uint64 RecursionMisses uint64 VerifiedInsns uint32 AttachBtfObjId BTFID AttachBtfId TypeID _ [4]byte } type SkLookup struct { _ structs.HostLayout Cookie uint64 Family uint32 Protocol uint32 RemoteIp4 [4]uint8 RemoteIp6 [16]uint8 RemotePort uint16 _ [2]byte LocalIp4 [4]uint8 LocalIp6 [16]uint8 LocalPort uint32 IngressIfindex uint32 _ [4]byte } type XdpMd struct { _ structs.HostLayout Data uint32 DataEnd uint32 DataMeta uint32 IngressIfindex uint32 RxQueueIndex uint32 EgressIfindex uint32 } type BtfGetFdByIdAttr struct { _ structs.HostLayout Id uint32 } func BtfGetFdById(attr *BtfGetFdByIdAttr) (*FD, error) { fd, err := BPF(BPF_BTF_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type BtfGetNextIdAttr struct { _ structs.HostLayout Id BTFID NextId BTFID } func BtfGetNextId(attr *BtfGetNextIdAttr) error { _, err := BPF(BPF_BTF_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type BtfLoadAttr struct { _ structs.HostLayout Btf TypedPointer[uint8] BtfLogBuf TypedPointer[uint8] BtfSize uint32 BtfLogSize uint32 BtfLogLevel uint32 BtfLogTrueSize uint32 BtfFlags uint32 BtfTokenFd int32 } func BtfLoad(attr *BtfLoadAttr) (*FD, error) { fd, err := BPF(BPF_BTF_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type EnableStatsAttr struct { _ structs.HostLayout Type uint32 } func EnableStats(attr *EnableStatsAttr) (*FD, error) { fd, err := BPF(BPF_ENABLE_STATS, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type IterCreateAttr struct { _ structs.HostLayout LinkFd uint32 Flags uint32 } func IterCreate(attr *IterCreateAttr) (*FD, error) { fd, err := BPF(BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateAttr struct { _ structs.HostLayout ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 TargetBtfId TypeID _ [44]byte } func LinkCreate(attr *LinkCreateAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateIterAttr struct { _ structs.HostLayout ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 IterInfo Pointer IterInfoLen uint32 _ [36]byte } func LinkCreateIter(attr *LinkCreateIterAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateKprobeMultiAttr struct { _ structs.HostLayout ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 KprobeMultiFlags uint32 Count uint32 Syms StringSlicePointer Addrs TypedPointer[uintptr] Cookies TypedPointer[uint64] _ [16]byte } func LinkCreateKprobeMulti(attr *LinkCreateKprobeMultiAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateNetfilterAttr struct { _ structs.HostLayout ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 Pf NetfilterProtocolFamily Hooknum NetfilterInetHook Priority int32 NetfilterFlags uint32 _ [32]byte } func LinkCreateNetfilter(attr *LinkCreateNetfilterAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateNetkitAttr struct { _ structs.HostLayout ProgFd uint32 TargetIfindex uint32 AttachType AttachType Flags uint32 RelativeFdOrId uint32 _ [4]byte ExpectedRevision uint64 _ [32]byte } func LinkCreateNetkit(attr *LinkCreateNetkitAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreatePerfEventAttr struct { _ structs.HostLayout ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 BpfCookie uint64 _ [40]byte } func LinkCreatePerfEvent(attr *LinkCreatePerfEventAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateTcxAttr struct { _ structs.HostLayout ProgFd uint32 TargetIfindex uint32 AttachType AttachType Flags uint32 RelativeFdOrId uint32 _ [4]byte ExpectedRevision uint64 _ [32]byte } func LinkCreateTcx(attr *LinkCreateTcxAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateTracingAttr struct { _ structs.HostLayout ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 TargetBtfId BTFID _ [4]byte Cookie uint64 _ [32]byte } func LinkCreateTracing(attr *LinkCreateTracingAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkCreateUprobeMultiAttr struct { _ structs.HostLayout ProgFd uint32 TargetFd uint32 AttachType AttachType Flags uint32 Path StringPointer Offsets TypedPointer[uint64] RefCtrOffsets TypedPointer[uint64] Cookies TypedPointer[uint64] Count uint32 UprobeMultiFlags uint32 Pid uint32 _ [4]byte } func LinkCreateUprobeMulti(attr *LinkCreateUprobeMultiAttr) (*FD, error) { fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkDetachAttr struct { _ structs.HostLayout LinkFd uint32 } func LinkDetach(attr *LinkDetachAttr) error { _, err := BPF(BPF_LINK_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type LinkGetFdByIdAttr struct { _ structs.HostLayout Id LinkID } func LinkGetFdById(attr *LinkGetFdByIdAttr) (*FD, error) { fd, err := BPF(BPF_LINK_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type LinkGetNextIdAttr struct { _ structs.HostLayout Id LinkID NextId LinkID } func LinkGetNextId(attr *LinkGetNextIdAttr) error { _, err := BPF(BPF_LINK_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type LinkUpdateAttr struct { _ structs.HostLayout LinkFd uint32 NewProgFd uint32 Flags uint32 OldProgFd uint32 } func LinkUpdate(attr *LinkUpdateAttr) error { _, err := BPF(BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapCreateAttr struct { _ structs.HostLayout MapType MapType KeySize uint32 ValueSize uint32 MaxEntries uint32 MapFlags uint32 InnerMapFd uint32 NumaNode uint32 MapName ObjName MapIfindex uint32 BtfFd uint32 BtfKeyTypeId TypeID BtfValueTypeId TypeID BtfVmlinuxValueTypeId TypeID MapExtra uint64 ValueTypeBtfObjFd int32 MapTokenFd int32 ExclProgHash uint64 ExclProgHashSize uint32 _ [4]byte } func MapCreate(attr *MapCreateAttr) (*FD, error) { fd, err := BPF(BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type MapDeleteBatchAttr struct { _ structs.HostLayout InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapDeleteBatch(attr *MapDeleteBatchAttr) error { _, err := BPF(BPF_MAP_DELETE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapDeleteElemAttr struct { _ structs.HostLayout MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapDeleteElem(attr *MapDeleteElemAttr) error { _, err := BPF(BPF_MAP_DELETE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapFreezeAttr struct { _ structs.HostLayout MapFd uint32 } func MapFreeze(attr *MapFreezeAttr) error { _, err := BPF(BPF_MAP_FREEZE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapGetFdByIdAttr struct { _ structs.HostLayout Id uint32 } func MapGetFdById(attr *MapGetFdByIdAttr) (*FD, error) { fd, err := BPF(BPF_MAP_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type MapGetNextIdAttr struct { _ structs.HostLayout Id uint32 NextId uint32 } func MapGetNextId(attr *MapGetNextIdAttr) error { _, err := BPF(BPF_MAP_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapGetNextKeyAttr struct { _ structs.HostLayout MapFd uint32 _ [4]byte Key Pointer NextKey Pointer } func MapGetNextKey(attr *MapGetNextKeyAttr) error { _, err := BPF(BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupAndDeleteBatchAttr struct { _ structs.HostLayout InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapLookupAndDeleteBatch(attr *MapLookupAndDeleteBatchAttr) error { _, err := BPF(BPF_MAP_LOOKUP_AND_DELETE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupAndDeleteElemAttr struct { _ structs.HostLayout MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapLookupAndDeleteElem(attr *MapLookupAndDeleteElemAttr) error { _, err := BPF(BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupBatchAttr struct { _ structs.HostLayout InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapLookupBatch(attr *MapLookupBatchAttr) error { _, err := BPF(BPF_MAP_LOOKUP_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapLookupElemAttr struct { _ structs.HostLayout MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapLookupElem(attr *MapLookupElemAttr) error { _, err := BPF(BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapUpdateBatchAttr struct { _ structs.HostLayout InBatch Pointer OutBatch Pointer Keys Pointer Values Pointer Count uint32 MapFd uint32 ElemFlags uint64 Flags uint64 } func MapUpdateBatch(attr *MapUpdateBatchAttr) error { _, err := BPF(BPF_MAP_UPDATE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type MapUpdateElemAttr struct { _ structs.HostLayout MapFd uint32 _ [4]byte Key Pointer Value Pointer Flags uint64 } func MapUpdateElem(attr *MapUpdateElemAttr) error { _, err := BPF(BPF_MAP_UPDATE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ObjGetAttr struct { _ structs.HostLayout Pathname StringPointer BpfFd uint32 FileFlags uint32 PathFd int32 _ [4]byte } func ObjGet(attr *ObjGetAttr) (*FD, error) { fd, err := BPF(BPF_OBJ_GET, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type ObjGetInfoByFdAttr struct { _ structs.HostLayout BpfFd uint32 InfoLen uint32 Info Pointer } func ObjGetInfoByFd(attr *ObjGetInfoByFdAttr) error { _, err := BPF(BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ObjPinAttr struct { _ structs.HostLayout Pathname StringPointer BpfFd uint32 FileFlags uint32 PathFd int32 _ [4]byte } func ObjPin(attr *ObjPinAttr) error { _, err := BPF(BPF_OBJ_PIN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgAttachAttr struct { _ structs.HostLayout TargetFdOrIfindex uint32 AttachBpfFd uint32 AttachType uint32 AttachFlags uint32 ReplaceBpfFd uint32 RelativeFdOrId uint32 ExpectedRevision uint64 } func ProgAttach(attr *ProgAttachAttr) error { _, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgBindMapAttr struct { _ structs.HostLayout ProgFd uint32 MapFd uint32 Flags uint32 } func ProgBindMap(attr *ProgBindMapAttr) error { _, err := BPF(BPF_PROG_BIND_MAP, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgDetachAttr struct { _ structs.HostLayout TargetFdOrIfindex uint32 AttachBpfFd uint32 AttachType uint32 AttachFlags uint32 _ [4]byte RelativeFdOrId uint32 ExpectedRevision uint64 } func ProgDetach(attr *ProgDetachAttr) error { _, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgGetFdByIdAttr struct { _ structs.HostLayout Id uint32 } func ProgGetFdById(attr *ProgGetFdByIdAttr) (*FD, error) { fd, err := BPF(BPF_PROG_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type ProgGetNextIdAttr struct { _ structs.HostLayout Id uint32 NextId uint32 } func ProgGetNextId(attr *ProgGetNextIdAttr) error { _, err := BPF(BPF_PROG_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgLoadAttr struct { _ structs.HostLayout ProgType ProgType InsnCnt uint32 Insns TypedPointer[uint8] License StringPointer LogLevel LogLevel LogSize uint32 LogBuf TypedPointer[uint8] KernVersion uint32 ProgFlags uint32 ProgName ObjName ProgIfindex uint32 ExpectedAttachType AttachType ProgBtfFd uint32 FuncInfoRecSize uint32 FuncInfo TypedPointer[uint8] FuncInfoCnt uint32 LineInfoRecSize uint32 LineInfo TypedPointer[uint8] LineInfoCnt uint32 AttachBtfId TypeID AttachBtfObjFd uint32 CoreReloCnt uint32 FdArray TypedPointer[int32] CoreRelos TypedPointer[uint8] CoreReloRecSize uint32 LogTrueSize uint32 ProgTokenFd int32 FdArrayCnt uint32 Signature uint64 SignatureSize uint32 KeyringId int32 } func ProgLoad(attr *ProgLoadAttr) (*FD, error) { fd, err := BPF(BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type ProgQueryAttr struct { _ structs.HostLayout TargetFdOrIfindex uint32 AttachType AttachType QueryFlags uint32 AttachFlags uint32 ProgIds TypedPointer[ProgramID] Count uint32 _ [4]byte ProgAttachFlags TypedPointer[ProgramID] LinkIds TypedPointer[LinkID] LinkAttachFlags TypedPointer[LinkID] Revision uint64 } func ProgQuery(attr *ProgQueryAttr) error { _, err := BPF(BPF_PROG_QUERY, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type ProgRunAttr struct { _ structs.HostLayout ProgFd uint32 Retval uint32 DataSizeIn uint32 DataSizeOut uint32 DataIn TypedPointer[uint8] DataOut TypedPointer[uint8] Repeat uint32 Duration uint32 CtxSizeIn uint32 CtxSizeOut uint32 CtxIn TypedPointer[uint8] CtxOut TypedPointer[uint8] Flags uint32 Cpu uint32 BatchSize uint32 _ [4]byte } func ProgRun(attr *ProgRunAttr) error { _, err := BPF(BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } type RawTracepointOpenAttr struct { _ structs.HostLayout Name StringPointer ProgFd uint32 _ [4]byte Cookie uint64 } func RawTracepointOpen(attr *RawTracepointOpenAttr) (*FD, error) { fd, err := BPF(BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } return NewFD(int(fd)) } type CgroupLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte CgroupId uint64 AttachType AttachType _ [36]byte } type EventLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte PerfEventType PerfEventType _ [4]byte Config uint64 EventType uint32 _ [4]byte Cookie uint64 _ [16]byte } type IterLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte TargetName TypedPointer[uint8] TargetNameLen uint32 } type KprobeLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte PerfEventType PerfEventType _ [4]byte FuncName TypedPointer[uint8] NameLen uint32 Offset uint32 Addr uint64 Missed uint64 Cookie uint64 } type KprobeMultiLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte Addrs TypedPointer[uint64] Count uint32 Flags uint32 Missed uint64 Cookies TypedPointer[uint64] _ [16]byte } type NetNsLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte NetnsIno uint32 AttachType AttachType _ [40]byte } type NetfilterLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte Pf NetfilterProtocolFamily Hooknum NetfilterInetHook Priority int32 Flags uint32 _ [32]byte } type NetkitLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte Ifindex uint32 AttachType AttachType _ [40]byte } type PerfEventLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte PerfEventType PerfEventType } type RawTracepointLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte TpName TypedPointer[uint8] TpNameLen uint32 _ [4]byte Cookie uint64 _ [24]byte } type TcxLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte Ifindex uint32 AttachType AttachType _ [40]byte } type TracepointLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte PerfEventType PerfEventType _ [4]byte TpName TypedPointer[uint8] NameLen uint32 _ [4]byte Cookie uint64 _ [16]byte } type TracingLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte AttachType AttachType TargetObjId uint32 TargetBtfId TypeID _ [4]byte Cookie uint64 _ [24]byte } type UprobeLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte PerfEventType PerfEventType _ [4]byte FileName TypedPointer[uint8] NameLen uint32 Offset uint32 Cookie uint64 RefCtrOffset uint64 _ [8]byte } type UprobeMultiLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte Path TypedPointer[uint8] Offsets TypedPointer[uint64] RefCtrOffsets TypedPointer[uint64] Cookies TypedPointer[uint64] PathSize uint32 Count uint32 Flags uint32 Pid uint32 } type XDPLinkInfo struct { _ structs.HostLayout Type LinkType Id LinkID ProgId uint32 _ [4]byte Ifindex uint32 _ [44]byte } golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/000077500000000000000000000000001520243672000223175ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/buffer.go000066400000000000000000000041721520243672000241230ustar00rootroot00000000000000package sysenc import ( "unsafe" "github.com/cilium/ebpf/internal/sys" ) type Buffer struct { ptr unsafe.Pointer // Size of the buffer. syscallPointerOnly if created from UnsafeBuffer or when using // zero-copy unmarshaling. size int } const syscallPointerOnly = -1 func newBuffer(buf []byte) Buffer { if len(buf) == 0 { return Buffer{} } return Buffer{unsafe.Pointer(&buf[0]), len(buf)} } // UnsafeBuffer constructs a Buffer for zero-copy unmarshaling. // // [Pointer] is the only valid method to call on such a Buffer. // Use [SyscallBuffer] instead if possible. func UnsafeBuffer(ptr unsafe.Pointer) Buffer { return Buffer{ptr, syscallPointerOnly} } // SyscallOutput prepares a Buffer for a syscall to write into. // // size is the length of the desired buffer in bytes. // The buffer may point at the underlying memory of dst, in which case [Unmarshal] // becomes a no-op. // // The contents of the buffer are undefined and may be non-zero. func SyscallOutput(dst any, size int) Buffer { if dstBuf := unsafeBackingMemory(dst); len(dstBuf) == size { buf := newBuffer(dstBuf) buf.size = syscallPointerOnly return buf } return newBuffer(make([]byte, size)) } // CopyTo copies the buffer into dst. // // Returns the number of copied bytes. func (b Buffer) CopyTo(dst []byte) int { return copy(dst, b.Bytes()) } // AppendTo appends the buffer onto dst. func (b Buffer) AppendTo(dst []byte) []byte { return append(dst, b.Bytes()...) } // Pointer returns the location where a syscall should write. func (b Buffer) Pointer() sys.Pointer { // NB: This deliberately ignores b.length to support zero-copy // marshaling / unmarshaling using unsafe.Pointer. return sys.UnsafePointer(b.ptr) } // Unmarshal the buffer into the provided value. func (b Buffer) Unmarshal(data any) error { if b.size == syscallPointerOnly { return nil } return Unmarshal(data, b.Bytes()) } // Bytes returns the buffer as a byte slice. Returns nil if the Buffer was // created using UnsafeBuffer or by zero-copy unmarshaling. func (b Buffer) Bytes() []byte { if b.size == syscallPointerOnly { return nil } return unsafe.Slice((*byte)(b.ptr), b.size) } golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/buffer_test.go000066400000000000000000000012541520243672000251600ustar00rootroot00000000000000package sysenc_test import ( "testing" "unsafe" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/sysenc" ) func TestZeroBuffer(t *testing.T) { var zero sysenc.Buffer qt.Assert(t, qt.Equals(zero.CopyTo(make([]byte, 1)), 0)) qt.Assert(t, qt.Equals(zero.Pointer(), sys.Pointer{})) qt.Assert(t, qt.IsNotNil(zero.Unmarshal(new(uint16)))) } func TestUnsafeBuffer(t *testing.T) { ptr := unsafe.Pointer(new(uint16)) buf := sysenc.UnsafeBuffer(ptr) qt.Assert(t, qt.Equals(buf.CopyTo(make([]byte, 1)), 0)) qt.Assert(t, qt.Equals(buf.Pointer(), sys.UnsafePointer(ptr))) qt.Assert(t, qt.IsNil(buf.Unmarshal(new(uint16)))) } golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/doc.go000066400000000000000000000001521520243672000234110ustar00rootroot00000000000000// Package sysenc provides efficient conversion of Go values to system // call interfaces. package sysenc golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/layout.go000066400000000000000000000017711520243672000241710ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found at https://go.dev/LICENSE. package sysenc import ( "reflect" "sync" ) var hasUnexportedFieldsCache sync.Map // map[reflect.Type]bool func hasUnexportedFields(typ reflect.Type) bool { switch typ.Kind() { case reflect.Slice, reflect.Array, reflect.Pointer: return hasUnexportedFields(typ.Elem()) case reflect.Struct: if unexported, ok := hasUnexportedFieldsCache.Load(typ); ok { return unexported.(bool) } unexported := false for i, n := 0, typ.NumField(); i < n; i++ { field := typ.Field(i) // Package binary allows _ fields but always writes zeroes into them. if (!field.IsExported() && field.Name != "_") || hasUnexportedFields(field.Type) { unexported = true break } } hasUnexportedFieldsCache.Store(typ, unexported) return unexported default: // NB: It's not clear what this means for Chan and so on. return false } } golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/layout_test.go000066400000000000000000000016311520243672000252230ustar00rootroot00000000000000package sysenc import ( "fmt" "reflect" "testing" "github.com/go-quicktest/qt" ) func TestHasUnexportedFields(t *testing.T) { for _, test := range []struct { value any result bool }{ {struct{ A any }{}, false}, {(*struct{ A any })(nil), false}, {([]struct{ A any })(nil), false}, {[1]struct{ A any }{}, false}, {struct{ _ any }{}, false}, {struct{ _ struct{ a any } }{}, true}, {(*struct{ _ any })(nil), false}, {([]struct{ _ any })(nil), false}, {[1]struct{ _ any }{}, false}, {struct{ a any }{}, true}, {(*struct{ a any })(nil), true}, {([]struct{ a any })(nil), true}, {[1]struct{ a any }{}, true}, {(*struct{ A []struct{ a any } })(nil), true}, {(*struct{ A [1]struct{ a any } })(nil), true}, } { t.Run(fmt.Sprintf("%T", test.value), func(t *testing.T) { have := hasUnexportedFields(reflect.TypeOf(test.value)) qt.Assert(t, qt.Equals(have, test.result)) }) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/marshal.go000066400000000000000000000104101520243672000242710ustar00rootroot00000000000000package sysenc import ( "encoding" "encoding/binary" "errors" "fmt" "reflect" "slices" "unsafe" "github.com/cilium/ebpf/internal" ) // Marshal turns data into a byte slice using the system's native endianness. // // If possible, avoids allocations by directly using the backing memory // of data. This means that the variable must not be modified for the lifetime // of the returned [Buffer]. // // Returns an error if the data can't be turned into a byte slice according to // the behaviour of [binary.Write]. func Marshal(data any, size int) (Buffer, error) { if data == nil { return Buffer{}, errors.New("can't marshal a nil value") } var buf []byte var err error switch value := data.(type) { case encoding.BinaryMarshaler: buf, err = value.MarshalBinary() case string: buf = unsafe.Slice(unsafe.StringData(value), len(value)) case []byte: buf = value case int16: buf = internal.NativeEndian.AppendUint16(make([]byte, 0, 2), uint16(value)) case uint16: buf = internal.NativeEndian.AppendUint16(make([]byte, 0, 2), value) case int32: buf = internal.NativeEndian.AppendUint32(make([]byte, 0, 4), uint32(value)) case uint32: buf = internal.NativeEndian.AppendUint32(make([]byte, 0, 4), value) case int64: buf = internal.NativeEndian.AppendUint64(make([]byte, 0, 8), uint64(value)) case uint64: buf = internal.NativeEndian.AppendUint64(make([]byte, 0, 8), value) default: if buf := unsafeBackingMemory(data); len(buf) == size { return newBuffer(buf), nil } buf, err = binary.Append(nil, internal.NativeEndian, value) } if err != nil { return Buffer{}, err } if len(buf) != size { return Buffer{}, fmt.Errorf("%T doesn't marshal to %d bytes", data, size) } return newBuffer(buf), nil } // Unmarshal a byte slice in the system's native endianness into data. // // Returns an error if buf can't be unmarshalled according to the behaviour // of [binary.Decode]. func Unmarshal(data interface{}, buf []byte) error { switch value := data.(type) { case encoding.BinaryUnmarshaler: return value.UnmarshalBinary(buf) case *string: *value = string(buf) return nil case *[]byte: // Backwards compat: unmarshaling into a slice replaces the whole slice. *value = slices.Clone(buf) return nil default: if dataBuf := unsafeBackingMemory(data); len(dataBuf) == len(buf) { copy(dataBuf, buf) return nil } n, err := binary.Decode(buf, internal.NativeEndian, value) if err != nil { return err } if n != len(buf) { return fmt.Errorf("unmarshaling %T doesn't consume all data", data) } return nil } } // unsafeBackingMemory returns the backing memory of data if it can be used // instead of calling into package binary. // // Returns nil if the value is not a pointer or a slice, or if it contains // padding or unexported fields. func unsafeBackingMemory(data any) []byte { if data == nil { return nil } value := reflect.ValueOf(data) var valueSize int switch value.Kind() { case reflect.Pointer: if value.IsNil() { return nil } if elemType := value.Type().Elem(); elemType.Kind() != reflect.Slice { valueSize = int(elemType.Size()) break } // We're dealing with a pointer to a slice. Dereference and // handle it like a regular slice. value = value.Elem() fallthrough case reflect.Slice: valueSize = int(value.Type().Elem().Size()) * value.Len() default: // Prevent Value.UnsafePointer from panicking. return nil } // Some nil pointer types currently crash binary.Size. Call it after our own // code so that the panic isn't reachable. // See https://github.com/golang/go/issues/60892 if size := binary.Size(data); size == -1 || size != valueSize { // The type contains padding or unsupported types. return nil } if hasUnexportedFields(reflect.TypeOf(data)) { return nil } // Reinterpret the pointer as a byte slice. This violates the unsafe.Pointer // rules because it's very unlikely that the source data has "an equivalent // memory layout". However, we can make it safe-ish because of the // following reasons: // - There is no alignment mismatch since we cast to a type with an // alignment of 1. // - There are no pointers in the source type so we don't upset the GC. // - The length is verified at runtime. return unsafe.Slice((*byte)(value.UnsafePointer()), valueSize) } golang-github-cilium-ebpf-0.21.0+ds1/internal/sysenc/marshal_test.go000066400000000000000000000141551520243672000253420ustar00rootroot00000000000000package sysenc import ( "bytes" "encoding/binary" "fmt" "math" "reflect" "testing" "github.com/go-quicktest/qt" "github.com/google/go-cmp/cmp/cmpopts" "github.com/cilium/ebpf/internal" ) type testcase struct { new func() any zeroAllocs bool // marshaling does not allocate } type struc struct { A uint64 B uint32 } type explicitPad struct { _ uint32 } func testcases() []testcase { return []testcase{ {func() any { return new([1]uint64) }, true}, {func() any { return new(int16) }, true}, {func() any { return new(uint16) }, true}, {func() any { return new(int32) }, true}, {func() any { return new(uint32) }, true}, {func() any { return new(int64) }, true}, {func() any { return new(uint64) }, true}, {func() any { return make([]byte, 9) }, true}, {func() any { return new(explicitPad) }, true}, {func() any { return make([]explicitPad, 0) }, false}, {func() any { return make([]explicitPad, 1) }, false}, {func() any { return make([]explicitPad, 2) }, false}, {func() any { return new(struc) }, false}, {func() any { return make([]struc, 0) }, false}, {func() any { return make([]struc, 1) }, false}, {func() any { return make([]struc, 2) }, false}, {func() any { return int16(math.MaxInt16) }, false}, {func() any { return uint16(math.MaxUint16) }, false}, {func() any { return int32(math.MaxInt32) }, false}, {func() any { return uint32(math.MaxUint32) }, false}, {func() any { return int64(math.MaxInt64) }, false}, {func() any { return uint64(math.MaxUint64) }, false}, {func() any { return struc{math.MaxUint64, math.MaxUint32} }, false}, } } func TestMarshal(t *testing.T) { for _, test := range testcases() { value := test.new() t.Run(fmt.Sprintf("%T", value), func(t *testing.T) { want, err := binary.Append(nil, internal.NativeEndian, value) qt.Assert(t, qt.IsNil(err)) have := make([]byte, len(want)) buf, err := Marshal(value, binary.Size(value)) if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(buf.CopyTo(have), len(want))) qt.Assert(t, qt.CmpEquals(have, want, cmpopts.EquateEmpty())) }) } } func TestMarshalAllocations(t *testing.T) { allocationsPerMarshal := func(t *testing.T, data any) float64 { size := binary.Size(data) return testing.AllocsPerRun(5, func() { _, err := Marshal(data, size) if err != nil { t.Fatal(err) } }) } for _, test := range testcases() { if !test.zeroAllocs { continue } value := test.new() t.Run(fmt.Sprintf("%T", value), func(t *testing.T) { qt.Assert(t, qt.Equals(allocationsPerMarshal(t, value), 0)) }) } } func TestUnmarshal(t *testing.T) { for _, test := range testcases() { value := test.new() if !canUnmarshalInto(value) { continue } t.Run(fmt.Sprintf("%T", value), func(t *testing.T) { want := test.new() buf := randomiseValue(t, want) qt.Assert(t, qt.IsNil(Unmarshal(value, buf))) qt.Assert(t, qt.DeepEquals(value, want)) }) } } func TestUnmarshalAllocations(t *testing.T) { allocationsPerUnmarshal := func(t *testing.T, data any, buf []byte) float64 { return testing.AllocsPerRun(5, func() { err := Unmarshal(data, buf) if err != nil { t.Fatal(err) } }) } for _, test := range testcases() { value := test.new() if !canUnmarshalInto(value) { continue } t.Run(fmt.Sprintf("%T", value), func(t *testing.T) { buf := make([]byte, binary.Size(value)) qt.Assert(t, qt.Equals(allocationsPerUnmarshal(t, value, buf), 0)) }) } } func TestUnsafeBackingMemory(t *testing.T) { marshalNative := func(t *testing.T, data any) []byte { t.Helper() buf, err := binary.Append(nil, internal.NativeEndian, data) qt.Assert(t, qt.IsNil(err)) return buf } for _, test := range []struct { name string value any }{ { "slice", []uint32{1, 2}, }, { "pointer to slice", &[]uint32{2}, }, { "pointer to array", &[2]uint64{}, }, { "pointer to int64", new(int64), }, { "pointer to struct", &struct { A, B uint16 C uint32 }{}, }, { "struct with explicit padding", &struct{ _ uint64 }{}, }, } { t.Run("valid: "+test.name, func(t *testing.T) { want := marshalNative(t, test.value) have := unsafeBackingMemory(test.value) qt.Assert(t, qt.DeepEquals(have, want)) }) } for _, test := range []struct { name string value any }{ { "nil", nil, }, { "nil slice", ([]byte)(nil), }, { "nil pointer", (*uint64)(nil), }, { "nil pointer to slice", (*[]uint32)(nil), }, { "nil pointer to array", (*[2]uint64)(nil), }, { "unexported field", &struct{ a uint64 }{}, }, { "struct containing pointer", &struct{ A *uint64 }{}, }, { "struct with trailing padding", &struc{}, }, { "struct with interspersed padding", &struct { B uint32 A uint64 }{}, }, { "padding between slice entries", &[]struc{{}}, }, { "padding between array entries", &[2]struc{}, }, } { t.Run("invalid: "+test.name, func(t *testing.T) { qt.Assert(t, qt.IsNil(unsafeBackingMemory(test.value))) }) } } func BenchmarkMarshal(b *testing.B) { for _, test := range testcases() { value := test.new() b.Run(fmt.Sprintf("%T", value), func(b *testing.B) { size := binary.Size(value) b.ReportAllocs() for b.Loop() { _, _ = Marshal(value, size) } }) } } func BenchmarkUnmarshal(b *testing.B) { for _, test := range testcases() { value := test.new() if !canUnmarshalInto(value) { continue } b.Run(fmt.Sprintf("%T", value), func(b *testing.B) { size := binary.Size(value) buf := make([]byte, size) b.ReportAllocs() for b.Loop() { _ = Unmarshal(value, buf) } }) } } func randomiseValue(tb testing.TB, value any) []byte { tb.Helper() size := binary.Size(value) if size == -1 { tb.Fatalf("Can't unmarshal into %T", value) } buf := make([]byte, size) for i := range buf { buf[i] = byte(i) } err := binary.Read(bytes.NewReader(buf), internal.NativeEndian, value) qt.Assert(tb, qt.IsNil(err)) return buf } func canUnmarshalInto(data any) bool { kind := reflect.TypeOf(data).Kind() return kind == reflect.Slice || kind == reflect.Pointer } golang-github-cilium-ebpf-0.21.0+ds1/internal/testdata/000077500000000000000000000000001520243672000226245ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/testdata/errno524.log000066400000000000000000000002101520243672000247000ustar00rootroot00000000000000JIT doesn't support bpf-to-bpf calls processed 39 insns (limit 1000000) max_states_per_insn 1 total_states 3 peak_states 3 mark_read 2 golang-github-cilium-ebpf-0.21.0+ds1/internal/testdata/invalid-R0.log000066400000000000000000000002371520243672000252360ustar00rootroot000000000000000: R1=ctx(id=0,off=0,imm=0) R10=fp0 0: (95) exit R0 !read_ok processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 golang-github-cilium-ebpf-0.21.0+ds1/internal/testdata/invalid-ctx-access.log000066400000000000000000000005131520243672000270070ustar00rootroot00000000000000arg#0 type is not a struct Unrecognized arg#0 type PTR ; int BPF_PROG(sys_recvfrom, struct pt_regs *regs) { 0: (79) r6 = *(u64 *)(r1 +0) func '__x64_sys_recvfrom' arg0 type FWD is not a struct invalid bpf_context access off=0 size=8 processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 golang-github-cilium-ebpf-0.21.0+ds1/internal/testdata/invalid-member.log000066400000000000000000000016231520243672000262240ustar00rootroot00000000000000CO-RE tracer failed: failed to load ebpf program: field MountEntry: program mount_entry: map .rodata: load BTF: invalid argument: magic: 0xeb9f version: 1 flags: 0x0 hdr_len: 24 type_off: 0 type_len: 16468 str_off: 16468 str_len: 12649 btf_total_size: 29141 [431] FWD rt_mutex_waiter struct [432] FWD sched_class struct [433] FWD seccomp_filter struct [434] FWD sem_undo_list struct [435] FWD sighand_struct struct [436] FWD signal_struct struct [437] FWD task_delay_info struct [438] FWD task_group struct [439] FWD time_namespace struct [440] FWD trace_event_call struct [441] FWD ucounts struct [442] FWD uprobe_task struct [443] FWD user_namespace struct [444] FWD userfaultfd_ctx struct [445] FWD uts_namespace struct [446] FWD vm_operations_struct struct [447] FWD vm_struct struct [52] STRUCT task_struct size=7744 vlen=218 cpus_mask type_id=109 bitfield_size=0 bits_offset=7744 Invalid member golang-github-cilium-ebpf-0.21.0+ds1/internal/testdata/issue-43.log000066400000000000000000000014251520243672000247050ustar00rootroot00000000000000magic: 0xeb9f version: 1 flags: 0x0 hdr_len: 24 type_off: 0 type_len: 536 str_off: 536 str_len: 582 btf_total_size: 1142 [1] STRUCT (anon) size=40 vlen=5 type type_id=2 bits_offset=0 key type_id=6 bits_offset=64 value type_id=6 bits_offset=128 max_entries type_id=2 bits_offset=192 map_flags type_id=2 bits_offset=256 [2] PTR (anon) type_id=4 [3] INT int size=4 bits_offset=0 nr_bits=32 encoding=SIGNED [4] ARRAY (anon) type_id=3 index_type_id=5 nr_elems=1 [5] INT __ARRAY_SIZE_TYPE__ size=4 bits_offset=0 nr_bits=32 encoding=(none) [6] PTR (anon) type_id=7 [7] TYPEDEF uint32_t type_id=8 [8] INT unsigned int size=4 bits_offset=0 nr_bits=32 encoding=(none) [9] VAR btf_map type_id=1 linkage=1 [10] FUNC_PROTO (anon) return=3 args=(3 arg) [11] FUNC helper_func2 type_id=10 vlen != 0 golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/000077500000000000000000000000001520243672000230535ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/bpffs_other.go000066400000000000000000000006711520243672000257070ustar00rootroot00000000000000//go:build !windows package testutils import ( "os" "testing" ) // TempBPFFS creates a temporary directory on a BPF FS. // // The directory is automatically cleaned up at the end of the test run. func TempBPFFS(tb testing.TB) string { tb.Helper() tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test") if err != nil { tb.Fatal("Create temporary directory on BPFFS:", err) } tb.Cleanup(func() { os.RemoveAll(tmp) }) return tmp } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/bpffs_windows.go000066400000000000000000000015661520243672000262640ustar00rootroot00000000000000package testutils import ( "errors" "math/rand" "path/filepath" "strconv" "strings" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/efw" ) // TempBPFFS creates a random prefix to use when pinning on Windows. func TempBPFFS(tb testing.TB) string { tb.Helper() path := filepath.Join("ebpf-go-test", strconv.Itoa(rand.Int())) path, err := efw.EbpfCanonicalizePinPath(path) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { tb.Helper() cursor := path for { next, _, err := efw.EbpfGetNextPinnedObjectPath(cursor, efw.EBPF_OBJECT_UNKNOWN) if errors.Is(err, efw.EBPF_NO_MORE_KEYS) { break } qt.Assert(tb, qt.IsNil(err)) if !strings.HasPrefix(next, path) { break } if err := efw.EbpfObjectUnpin(next); err != nil { tb.Errorf("Failed to unpin %s: %s", next, err) } cursor = next } }) return path } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/bpffs_windows_test.go000066400000000000000000000033151520243672000273150ustar00rootroot00000000000000package testutils_test import ( "bytes" "os" "path/filepath" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" ) func TestTempBPFFS(t *testing.T) { var progPath, mapPath string t.Run("pin", func(t *testing.T) { tmp := testutils.TempBPFFS(t) progPath = filepath.Join(tmp, "prog") mapPath = filepath.Join(tmp, "map") var buffer bytes.Buffer insns := asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), } err := insns.Marshal(&buffer, internal.NativeEndian) qt.Assert(t, qt.IsNil(err)) progFd, err := sys.ProgLoad(&sys.ProgLoadAttr{ ProgType: 999, // SAMPLE License: sys.NewStringPointer(""), InsnCnt: uint32(buffer.Len() / asm.InstructionSize), Insns: sys.SlicePointer(buffer.Bytes()), }) qt.Assert(t, qt.IsNil(err)) defer progFd.Close() err = sys.ObjPin(&sys.ObjPinAttr{ BpfFd: progFd.Uint(), Pathname: sys.NewStringPointer(progPath), }) qt.Assert(t, qt.IsNil(err)) mapFd, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: 2, // ARRAY KeySize: 4, ValueSize: 4, MaxEntries: 1, }) qt.Assert(t, qt.IsNil(err)) defer mapFd.Close() err = sys.ObjPin(&sys.ObjPinAttr{ BpfFd: progFd.Uint(), Pathname: sys.NewStringPointer(mapPath), }) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(mapFd.Close())) }) _, err := sys.ObjGet(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(progPath), }) qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) _, err = sys.ObjGet(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(mapPath), }) qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/cap.go000066400000000000000000000053641520243672000241550ustar00rootroot00000000000000package testutils import ( "runtime" "testing" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/unix" ) type Capability int // Mirrors of constants from x/sys/unix const ( CAP_NET_ADMIN Capability = 12 CAP_SYS_ADMIN Capability = 21 CAP_SYS_RESOURCE Capability = 24 CAP_PERFMON Capability = 38 CAP_BPF Capability = 39 ) // WithCapabilities runs `f` with only the given capabilities // in the effective set. This allows us to assert that certain operations // only require specific capabilities. // // The code in `f` and any code called by `f` must NOT call [runtime.LockOSThread], // as this could leave the current goroutine permanently pinned to an OS thread. // It must also not create any goroutines of its own, as that will result in a new // OS thread being created that may or may not inherit the new capabilities of its // parent, and will later be released into the schedulable pool of threads available // for goroutine scheduling. // // Warning: on non-linux platforms, this function calls through to `f` without // side effects. func WithCapabilities(tb testing.TB, caps []Capability, f func()) { tb.Helper() if !platform.IsLinux { f() return } runtime.LockOSThread() defer runtime.UnlockOSThread() orig, err := capget() if err != nil { tb.Fatal("Can't get capabilities:", err) } var set capUserData for _, cap := range caps { set.Effective |= 1 << uint(cap) } set.Permitted = orig.Permitted if err := capset(set); err != nil { tb.Fatal("Can't set capabilities:", err) } f() if err := capset(orig); err != nil { tb.Fatal("Can't restore capabilities:", err) } } type capUserData struct { Effective uint64 Permitted uint64 Inheritable uint64 } func capget() (capUserData, error) { var hdr = &unix.CapUserHeader{ Version: unix.LINUX_CAPABILITY_VERSION_3, } var data [2]unix.CapUserData err := unix.Capget(hdr, &data[0]) if err != nil { return capUserData{}, err } return capUserData{ Effective: uint64(data[0].Effective) | uint64(data[1].Effective)<<32, Permitted: uint64(data[0].Permitted) | uint64(data[1].Permitted)<<32, Inheritable: uint64(data[0].Inheritable) | uint64(data[1].Inheritable)<<32, }, err } func capset(data capUserData) error { var hdr = &unix.CapUserHeader{ Version: unix.LINUX_CAPABILITY_VERSION_3, } var linuxData [2]unix.CapUserData linuxData[0].Effective = uint32(data.Effective & 0xFFFFFFFF) linuxData[0].Permitted = uint32(data.Permitted & 0xFFFFFFFF) linuxData[0].Inheritable = uint32(data.Inheritable & 0xFFFFFFFF) linuxData[1].Effective = uint32(data.Effective >> 32) linuxData[1].Permitted = uint32(data.Permitted >> 32) linuxData[1].Inheritable = uint32(data.Inheritable >> 32) return unix.Capset(hdr, &linuxData[0]) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/cgroup.go000066400000000000000000000021241520243672000247000ustar00rootroot00000000000000package testutils import ( "errors" "os" "strings" "sync" "testing" "github.com/cilium/ebpf/internal/unix" ) var cgroup2Path = sync.OnceValues(func() (string, error) { mounts, err := os.ReadFile("/proc/mounts") if err != nil { return "", err } for _, line := range strings.Split(string(mounts), "\n") { mount := strings.SplitN(line, " ", 3) if mount[0] == "cgroup2" { return mount[1], nil } continue } return "", errors.New("cgroup2 not mounted") }) func CreateCgroup(tb testing.TB) *os.File { tb.Helper() cg2, err := cgroup2Path() if err != nil { tb.Fatal("Can't locate cgroup2 mount:", err) } cgdir, err := os.MkdirTemp(cg2, "ebpf-link") if err != nil { tb.Fatal("Can't create cgroupv2:", err) } cgroup, err := os.Open(cgdir) if err != nil { os.Remove(cgdir) tb.Fatal(err) } tb.Cleanup(func() { cgroup.Close() os.Remove(cgdir) }) return cgroup } func GetCgroupIno(t *testing.T, cgroup *os.File) uint64 { cgroupStat := unix.Stat_t{} err := unix.Fstat(int(cgroup.Fd()), &cgroupStat) if err != nil { t.Fatal(err) } return cgroupStat.Ino } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/chan.go000066400000000000000000000005721520243672000243170ustar00rootroot00000000000000package testutils import ( "testing" "time" ) // WaitChan waits for a value to be sent on a channel, or for a timeout to // occur. If the timeout is reached, the test will fail. func WaitChan[T any](tb testing.TB, ch <-chan T, timeout time.Duration) { tb.Helper() select { case <-ch: return case <-time.After(timeout): tb.Fatalf("timeout waiting for channel") } } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/checkers.go000066400000000000000000000071311520243672000251730ustar00rootroot00000000000000package testutils import ( "bytes" "fmt" "reflect" "github.com/go-quicktest/qt" ) // Contains checks if interface value I is of type T. Use with qt.Satisfies: // // qt.Assert(t, qt.Satisfies(p, testutils.Contains[*ebpf.Program])) func Contains[T, I any](i I) bool { _, ok := any(i).(T) return ok } // IsDeepCopy checks that got is a deep copy of want. // // All primitive values must be equal, but pointers must be distinct. // This is different from [reflect.DeepEqual] which will accept equal pointer values. // That is, reflect.DeepEqual(a, a) is true, while IsDeepCopy(a, a) is false. func IsDeepCopy[T any](got, want T) qt.Checker { return &deepCopyChecker[T]{got, want, make(map[pair]struct{})} } type pair struct { got, want reflect.Value } type deepCopyChecker[T any] struct { got, want T visited map[pair]struct{} } func (dcc *deepCopyChecker[T]) Check(_ func(key string, value any)) error { return dcc.check(reflect.ValueOf(dcc.got), reflect.ValueOf(dcc.want)) } func (dcc *deepCopyChecker[T]) check(got, want reflect.Value) error { switch want.Kind() { case reflect.Interface: return dcc.check(got.Elem(), want.Elem()) case reflect.Pointer: if got.IsNil() && want.IsNil() { return nil } if got.IsNil() { return fmt.Errorf("expected non-nil pointer") } if want.IsNil() { return fmt.Errorf("expected nil pointer") } if got.UnsafePointer() == want.UnsafePointer() { return fmt.Errorf("equal pointer values") } switch want.Type() { case reflect.TypeOf((*bytes.Reader)(nil)): // bytes.Reader doesn't allow modifying it's contents, so we // allow a shallow copy. return nil } if _, ok := dcc.visited[pair{got, want}]; ok { // Deal with recursive types. return nil } dcc.visited[pair{got, want}] = struct{}{} return dcc.check(got.Elem(), want.Elem()) case reflect.Slice: if got.IsNil() && want.IsNil() { return nil } if got.IsNil() { return fmt.Errorf("expected non-nil slice") } if want.IsNil() { return fmt.Errorf("expected nil slice") } if got.Len() != want.Len() { return fmt.Errorf("expected %d elements, got %d", want.Len(), got.Len()) } if want.Len() == 0 { return nil } if got.UnsafePointer() == want.UnsafePointer() { return fmt.Errorf("equal backing memory") } fallthrough case reflect.Array: for i := 0; i < want.Len(); i++ { if err := dcc.check(got.Index(i), want.Index(i)); err != nil { return fmt.Errorf("index %d: %w", i, err) } } return nil case reflect.Struct: for i := 0; i < want.NumField(); i++ { if err := dcc.check(got.Field(i), want.Field(i)); err != nil { return fmt.Errorf("%q: %w", want.Type().Field(i).Name, err) } } return nil case reflect.Map: if got.Len() != want.Len() { return fmt.Errorf("expected %d items, got %d", want.Len(), got.Len()) } if got.UnsafePointer() == want.UnsafePointer() { return fmt.Errorf("maps are equal") } iter := want.MapRange() for iter.Next() { key := iter.Key() got := got.MapIndex(iter.Key()) if !got.IsValid() { return fmt.Errorf("key %v is missing", key) } want := iter.Value() if err := dcc.check(got, want); err != nil { return fmt.Errorf("key %v: %w", key, err) } } return nil case reflect.Chan, reflect.UnsafePointer: return fmt.Errorf("%s is not supported", want.Type()) default: // Compare by value as usual. if !got.Equal(want) { return fmt.Errorf("%#v is not equal to %#v", got, want) } return nil } } func (dcc *deepCopyChecker[T]) Args() []qt.Arg { return []qt.Arg{ {Name: "got", Value: dcc.got}, {Name: "want", Value: dcc.want}, } } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/checkers_test.go000066400000000000000000000043611520243672000262340ustar00rootroot00000000000000package testutils import ( "testing" "github.com/go-quicktest/qt" ) func TestIsDeepCopy(t *testing.T) { type s struct { basic int array [1]*int array0 [0]int ptr *int slice []*int ifc any m map[*int]*int rec *s } key := 1 copy := func() *s { v := &s{ 0, [...]*int{new(int)}, [...]int{}, new(int), []*int{new(int)}, new(int), map[*int]*int{&key: new(int)}, nil, } v.rec = v return v } a, b := copy(), copy() qt.Check(t, qt.IsNil(IsDeepCopy(a, b).Check(nil))) a.basic++ qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"basic": .*`)) a = copy() (*a.array[0])++ qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"array": index 0: .*`)) a = copy() a.array[0] = nil qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"array": index 0: .*`)) a = copy() a.array = b.array qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"array": index 0: .*`)) a = copy() (*a.ptr)++ qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"ptr": .*`)) a = copy() a.ptr = b.ptr qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"ptr": .*`)) a = copy() (*a.slice[0])++ qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"slice": .*`)) a = copy() a.slice[0] = nil qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"slice": .*`)) a = copy() a.slice = nil qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"slice": .*`)) a = copy() a.slice = b.slice qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"slice": .*`)) a = copy() *(a.ifc.(*int))++ qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"ifc": .*`)) a = copy() a.ifc = b.ifc qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"ifc": .*`)) a = copy() a.rec = b.rec qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"rec": .*`)) a = copy() a.m = b.m qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"m": .*`)) a = copy() (*a.m[&key])++ qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"m": .*`)) a = copy() a.m[new(int)] = new(int) qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"m": .*`)) a = copy() delete(a.m, &key) qt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `"m": .*`)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/cpu.go000066400000000000000000000011631520243672000241720ustar00rootroot00000000000000package testutils import ( "runtime" "testing" "github.com/cilium/ebpf/internal/unix" "github.com/go-quicktest/qt" ) // LockOSThreadToSingleCPU force the current goroutine to run on a single CPU. func LockOSThreadToSingleCPU(tb testing.TB) { tb.Helper() runtime.LockOSThread() tb.Cleanup(runtime.UnlockOSThread) var old unix.CPUSet err := unix.SchedGetaffinity(0, &old) qt.Assert(tb, qt.IsNil(err)) // Schedule test to run on only CPU 0 var first unix.CPUSet first.Set(0) err = unix.SchedSetaffinity(0, &first) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { _ = unix.SchedSetaffinity(0, &old) }) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/fd_other.go000066400000000000000000000004531520243672000251760ustar00rootroot00000000000000//go:build !windows package testutils import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/unix" ) func DupFD(tb testing.TB, fd int) int { tb.Helper() dup, err := unix.FcntlInt(uintptr(fd), unix.F_DUPFD_CLOEXEC, 1) qt.Assert(tb, qt.IsNil(err)) return dup } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/fd_windows.go000066400000000000000000000003701520243672000255450ustar00rootroot00000000000000package testutils import ( "testing" "github.com/cilium/ebpf/internal/efw" "github.com/go-quicktest/qt" ) func DupFD(tb testing.TB, fd int) int { tb.Helper() dup, err := efw.EbpfDuplicateFd(fd) qt.Assert(tb, qt.IsNil(err)) return dup } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/feature.go000066400000000000000000000102551520243672000250400ustar00rootroot00000000000000package testutils import ( "encoding/binary" "errors" "os" "runtime" "strings" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" ) const ( ignoreVersionEnvVar = "EBPF_TEST_IGNORE_VERSION" ) func CheckFeatureTest(t *testing.T, fn func() error) { t.Helper() checkFeatureTestError(t, fn()) } func checkFeatureTestError(t *testing.T, err error) { t.Helper() if err == nil { return } if errors.Is(err, internal.ErrNotSupportedOnOS) { t.Skip(err) } var ufe *internal.UnsupportedFeatureError if errors.As(err, &ufe) { checkVersion(t, ufe) } else { t.Error("Feature test failed:", err) } } func CheckFeatureMatrix[K comparable](t *testing.T, fm internal.FeatureMatrix[K]) { t.Helper() for key, ft := range fm { t.Run(ft.Name, func(t *testing.T) { checkFeatureTestError(t, fm.Result(key)) }) } } func SkipIfNotSupported(tb testing.TB, err error) { tb.Helper() if err == internal.ErrNotSupported { tb.Fatal("Unwrapped ErrNotSupported") } var ufe *internal.UnsupportedFeatureError if errors.As(err, &ufe) { checkVersion(tb, ufe) tb.Skip(ufe.Error()) } if errors.Is(err, internal.ErrNotSupported) { tb.Skip(err.Error()) } } func SkipIfNotSupportedOnOS(tb testing.TB, err error) { tb.Helper() if err == internal.ErrNotSupportedOnOS { tb.Fatal("Unwrapped ErrNotSupportedOnOS") } if errors.Is(err, internal.ErrNotSupportedOnOS) { tb.Skip(err.Error()) } } func checkVersion(tb testing.TB, ufe *internal.UnsupportedFeatureError) { if ufe.MinimumVersion.Unspecified() { return } tb.Helper() if ignoreVersionCheck(tb.Name()) { tb.Logf("Ignoring error due to %s: %s", ignoreVersionEnvVar, ufe.Error()) return } if !isPlatformVersionLessThan(tb, ufe.MinimumVersion, platformVersion(tb)) { tb.Fatalf("Feature '%s' isn't supported even though kernel is newer than %s", ufe.Name, ufe.MinimumVersion) } } // Skip a test based on the Linux version we are running on. // // Warning: this function does not have an effect on platforms other than Linux. func SkipOnOldKernel(tb testing.TB, minVersion, feature string) { tb.Helper() if !platform.IsLinux { tb.Logf("Ignoring version constraint %s for %s on %s", minVersion, feature, runtime.GOOS) return } if IsVersionLessThan(tb, minVersion) { tb.Skipf("Test requires at least kernel %s (due to missing %s)", minVersion, feature) } } // Check whether the current runtime version is less than some minimum. func IsVersionLessThan(tb testing.TB, minVersions ...string) bool { tb.Helper() version, err := platform.SelectVersion(minVersions) qt.Assert(tb, qt.IsNil(err)) if version == "" { // No matching version means that the platform // doesn't support whatever feature. return true } minv, err := internal.NewVersion(version) if err != nil { tb.Fatalf("Invalid version %s: %s", version, err) } return isPlatformVersionLessThan(tb, minv, platformVersion(tb)) } func isPlatformVersionLessThan(tb testing.TB, minv, runv internal.Version) bool { tb.Helper() key := "CI_MAX_KERNEL_VERSION" if platform.IsWindows { key = "CI_MAX_EFW_VERSION" } if max := os.Getenv(key); max != "" { maxv, err := internal.NewVersion(max) if err != nil { tb.Fatalf("Invalid version %q in %s: %s", max, key, err) } if maxv.Less(minv) { tb.Fatalf("Test for %s will never execute on CI since %s is the most recent runtime", minv, maxv) } } return runv.Less(minv) } // ignoreVersionCheck checks whether to omit the version check for a test. // // It reads a comma separated list of test names from an environment variable. // // For example: // // EBPF_TEST_IGNORE_VERSION=TestABC,TestXYZ go test ... func ignoreVersionCheck(tName string) bool { tNames := os.Getenv(ignoreVersionEnvVar) if tNames == "" { return false } ignored := strings.Split(tNames, ",") for _, n := range ignored { if strings.TrimSpace(n) == tName { return true } } return false } // SkipNonNativeEndian skips the test or benchmark if bo doesn't match the // host's native endianness. func SkipNonNativeEndian(tb testing.TB, bo binary.ByteOrder) { tb.Helper() if bo != internal.NativeEndian { tb.Skip("Skipping due to non-native endianness") } } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/feature_other.go000066400000000000000000000004421520243672000262360ustar00rootroot00000000000000//go:build !windows package testutils import ( "testing" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/linux" ) func platformVersion(tb testing.TB) internal.Version { tb.Helper() v, err := linux.KernelVersion() if err != nil { tb.Fatal(err) } return v } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/feature_test.go000066400000000000000000000036421520243672000261010ustar00rootroot00000000000000package testutils import ( "testing" "github.com/go-quicktest/qt" ) func TestIgnoreKernelVersionCheckWhenEnvVarIsSet(t *testing.T) { tests := []struct { name string toIgnoreNamesEnvValue string testName string ignoreKernelVersionCheck bool }{ { name: "should NOT ignore kernel version check if environment var set to empty string", toIgnoreNamesEnvValue: "", testName: "TestABC", ignoreKernelVersionCheck: false, }, { name: "should ignore kernel version check if environment var set to skip test name with single value", toIgnoreNamesEnvValue: "TestABC", testName: "TestABC", ignoreKernelVersionCheck: true, }, { name: "should match test name when multiple comma separated names list is provided", toIgnoreNamesEnvValue: "TestABC,TestXYZ", testName: "TestXYZ", ignoreKernelVersionCheck: true, }, { name: "should NOT match test name when multiple comma separated names list is provided but name is not present in list", toIgnoreNamesEnvValue: "TestABC,TestXYZ", testName: "TestPQR", ignoreKernelVersionCheck: false, }, { name: "should match test name if names list has leading/trailing spaces", toIgnoreNamesEnvValue: "TestABC, TestXYZ , TestPQR", testName: "TestXYZ", ignoreKernelVersionCheck: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Setenv(ignoreVersionEnvVar, tt.toIgnoreNamesEnvValue) if got := ignoreVersionCheck(tt.testName); got != tt.ignoreKernelVersionCheck { t.Errorf("ignoreKernelVersionCheck() = %v, want %v", got, tt.ignoreKernelVersionCheck) } }) } } func TestPlatformVersion(t *testing.T) { qt.Assert(t, qt.IsFalse(platformVersion(t).Unspecified())) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/feature_windows.go000066400000000000000000000007231520243672000266110ustar00rootroot00000000000000package testutils import ( "os" "testing" "github.com/cilium/ebpf/internal" "github.com/go-quicktest/qt" ) func platformVersion(tb testing.TB) internal.Version { tb.Helper() versionStr, ok := os.LookupEnv("CI_EFW_VERSION") qt.Assert(tb, qt.IsTrue(ok), qt.Commentf("Missing CI_EFW_VERSION environment variable")) version, err := internal.NewVersion(versionStr) qt.Assert(tb, qt.IsNil(err), qt.Commentf("Parse eBPF for Windows version")) return version } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/glob.go000066400000000000000000000030301520243672000243210ustar00rootroot00000000000000package testutils import ( "fmt" "path/filepath" "strings" "testing" "golang.org/x/sys/cpu" ) // Files calls fn for each given file. // // The function errors out if the pattern matches no files. func Files(t *testing.T, files []string, fn func(*testing.T, string)) { t.Helper() if len(files) == 0 { t.Fatalf("No files given") } for _, f := range files { file := f // force copy name := filepath.Base(file) t.Run(name, func(t *testing.T) { fn(t, file) }) } } // Glob finds files matching a pattern. // // The pattern should may include full path. Excludes use the same syntax as // pattern, but are only applied to the basename instead of the full path. func Glob(tb testing.TB, pattern string, excludes ...string) []string { tb.Helper() files, err := filepath.Glob(pattern) if err != nil { tb.Fatal("Can't glob files:", err) } if len(excludes) == 0 { return files } var filtered []string nextFile: for _, file := range files { base := filepath.Base(file) for _, exclude := range excludes { if matched, err := filepath.Match(exclude, base); err != nil { tb.Fatal(err) } else if matched { continue nextFile } } filtered = append(filtered, file) } return filtered } // NativeFile substitutes %s with an abbreviation of the host endianness. func NativeFile(tb testing.TB, path string) string { tb.Helper() if !strings.Contains(path, "%s") { tb.Fatalf("File %q doesn't contain %%s", path) } if cpu.IsBigEndian { return fmt.Sprintf(path, "eb") } return fmt.Sprintf(path, "el") } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/netns_linux.go000066400000000000000000000123071520243672000257530ustar00rootroot00000000000000//go:build linux // The netns implementation in this file was taken from cilium/cilium. package testutils import ( "fmt" "os" "runtime" "testing" "golang.org/x/sync/errgroup" "github.com/cilium/ebpf/internal/unix" ) type NetNS struct { f *os.File } // NewNetNS returns a new network namespace. func NewNetNS(tb testing.TB) *NetNS { tb.Helper() ns, err := newNetNS() if err != nil { tb.Fatal(err) } tb.Cleanup(func() { ns.close() }) return ns } // Do runs the provided func in the netns without changing the calling thread's // netns. // // The code in f and any code called by f must NOT call [runtime.LockOSThread], // as this could leave the goroutine created by Do permanently pinned to an OS // thread. func (h *NetNS) Do(f func() error) error { // Start the func in a new goroutine and lock it to an exclusive thread. This // ensures that if execution of the goroutine fails unexpectedly before we // call UnlockOSThread, the go runtime will ensure the underlying OS thread is // disposed of, rather than reused in a potentially undefined state. // // See also: https://pkg.go.dev/runtime#UnlockOSThread var g errgroup.Group g.Go(func() error { // Lock the newly-created goroutine to the OS thread it's running on so we // can safely move it into another network namespace. (per-thread state) restoreUnlock, err := lockOSThread() if err != nil { return err } if err := set(h.f); err != nil { return fmt.Errorf("set netns: %w (terminating OS thread)", err) } ferr := f() // Attempt to restore the underlying OS thread to its original network // namespace and unlock the running goroutine from its OS thread. Any // failures during this process will leave the goroutine locked, making the // underlying OS thread terminate when this function returns. if err := restoreUnlock(); err != nil { return fmt.Errorf("restore original netns: %w (terminating OS thread)", err) } return ferr }) return g.Wait() } func newNetNS() (*NetNS, error) { var f *os.File // Perform network namespace creation in a new goroutine to give us the // possibility of terminating the underlying OS thread (by terminating the // goroutine) if something goes wrong. var g errgroup.Group g.Go(func() error { restoreUnlock, err := lockOSThread() if err != nil { return fmt.Errorf("lock OS thread: %w", err) } // Move the underlying OS thread to a new network namespace. This can be // undone by calling restoreUnlock(). if err := unshare(); err != nil { return fmt.Errorf("create new netns: %w", err) } // Take out a reference to the new netns. f, err = getCurrent() if err != nil { return fmt.Errorf("get current netns: %w (terminating OS thread)", err) } // Restore the OS thread to its original network namespace or implicitly // terminate it if something went wrong. if err := restoreUnlock(); err != nil { return fmt.Errorf("restore current netns: %w (terminating OS thread)", err) } return nil }) if err := g.Wait(); err != nil { return nil, err } ns := &NetNS{f: f} // Prevent resource leaks by eventually closing the underlying file descriptor // after ns is garbage collected. runtime.SetFinalizer(ns, (*NetNS).close) return ns, nil } func (h *NetNS) close() error { if h.f == nil { return nil } // Close closes the handle to the network namespace. This does not necessarily // mean destroying the network namespace itself, which only happens when all // references to it are gone and all of its processes have been terminated. if err := h.f.Close(); err != nil { return err } h.f = nil return nil } func lockOSThread() (func() error, error) { runtime.LockOSThread() orig, err := getCurrent() if err != nil { runtime.UnlockOSThread() return nil, fmt.Errorf("get current namespace: %w", err) } return func() error { defer orig.Close() if err := set(orig); err != nil { // We didn't manage to restore the OS thread to its original namespace. // Don't unlock the current goroutine from its thread, so the thread will // terminate when the current goroutine does. return err } // Original netns was restored, release the OS thread back into the // schedulable pool. runtime.UnlockOSThread() return nil }, nil } // unshare moves the calling OS thread of the calling goroutine to a new network // namespace. Must only be called after a prior call to lockOSThread(). func unshare() error { if err := unix.Unshare(unix.CLONE_NEWNET); err != nil { return err } return nil } // set sets the underlying OS thread of the calling goroutine to the netns // pointed at by f. func set(f *os.File) error { return unix.Setns(int(f.Fd()), unix.CLONE_NEWNET) } // getCurrent gets a file descriptor to the current thread network namespace. func getCurrent() (*os.File, error) { return getFromThread(os.Getpid(), unix.Gettid()) } // getFromPath gets a file descriptor to the network namespace pinned at path. func getFromPath(path string) (*os.File, error) { return os.OpenFile(path, unix.O_RDONLY|unix.O_CLOEXEC, 0) } // getFromThread gets a file descriptor to the network namespace of a given pid // and tid. func getFromThread(pid, tid int) (*os.File, error) { return getFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/netns_other.go000066400000000000000000000005141520243672000257320ustar00rootroot00000000000000//go:build !linux // This file is a stub to allow netns to be compiled on non-Linux platforms. package testutils import ( "testing" "github.com/cilium/ebpf/internal" ) type NetNS struct { } func NewNetNS(tb testing.TB) *NetNS { return nil } func (h *NetNS) Do(f func() error) error { return internal.ErrNotSupportedOnOS } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/programs.go000066400000000000000000000007321520243672000252360ustar00rootroot00000000000000package testutils import ( "fmt" "os" "testing" ) func ClangBin(tb testing.TB) string { tb.Helper() if testing.Short() { tb.Skip("Not compiling with -short") } // Use a floating clang version for local development, but allow CI to run // against oldest supported clang. clang := "clang" if minVersion := os.Getenv("CI_MIN_CLANG_VERSION"); minVersion != "" { clang = fmt.Sprintf("clang-%s", minVersion) } tb.Log("Testing against", clang) return clang } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/rlimit.go000066400000000000000000000005651520243672000247100ustar00rootroot00000000000000package testutils import ( "fmt" "os" "github.com/cilium/ebpf/rlimit" ) func init() { // Increase the memlock for all tests unconditionally. It's a great source of // weird bugs, since different distros have different default limits. if err := rlimit.RemoveMemlock(); err != nil { fmt.Fprintln(os.Stderr, "WARNING: Failed to adjust rlimit, tests may fail") } } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/seed.go000066400000000000000000000010471520243672000243240ustar00rootroot00000000000000package testutils import ( "math/rand" "os" "strconv" "sync" "testing" "time" ) var randSeed struct { value int64 once sync.Once } func Rand(tb testing.TB) *rand.Rand { randSeed.once.Do(func() { randSeed.value = time.Now().UnixMicro() }) seed := randSeed.value if seedStr, ok := os.LookupEnv("TEST_SEED"); ok { var err error seed, err = strconv.ParseInt(seedStr, 0, 64) if err != nil { tb.Fatal("Parse TEST_SEED environment variable:", err) } } tb.Logf("TEST_SEED=%d\n", seed) return rand.New(rand.NewSource(seed)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/testmain/000077500000000000000000000000001520243672000246775ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/testmain/fd_trace.go000066400000000000000000000046651520243672000270100ustar00rootroot00000000000000package testmain import ( "bytes" "fmt" "os" "runtime" "sync" "sync/atomic" ) // foundLeak is atomic since the GC may collect objects in parallel. var foundLeak atomic.Bool func onLeakFD(fs *runtime.Frames) { foundLeak.Store(true) fmt.Fprintln(os.Stderr, "leaked fd created at:") fmt.Fprintln(os.Stderr, formatFrames(fs)) } // fds is a registry of all file descriptors wrapped into sys.fds that were // created while an fd tracer was active. var fds *sync.Map // map[int]*runtime.Frames // TraceFD associates raw with the current execution stack. // // skip controls how many entries of the stack the function should skip. func TraceFD(raw int, skip int) { if fds == nil { return } // Attempt to store the caller's stack for the given fd value. // Panic if fds contains an existing stack for the fd. old, exist := fds.LoadOrStore(raw, callersFrames(skip)) if exist { f := old.(*runtime.Frames) panic(fmt.Sprintf("found existing stack for fd %d:\n%s", raw, formatFrames(f))) } } // ForgetFD removes any existing association for raw. func ForgetFD(raw int) { if fds != nil { fds.Delete(raw) } } // LeakFD indicates that raw was leaked. // // Calling the function with a value that was not passed to [TraceFD] before // is undefined. func LeakFD(raw int) { if fds == nil { return } // Invoke the fd leak callback. Calls LoadAndDelete to guarantee the callback // is invoked at most once for one sys.FD allocation, runtime.Frames can only // be unwound once. f, ok := fds.LoadAndDelete(raw) if ok { onLeakFD(f.(*runtime.Frames)) } } // flushFrames removes all elements from fds and returns them as a slice. This // deals with the fact that a runtime.Frames can only be unwound once using // Next(). func flushFrames() []*runtime.Frames { var frames []*runtime.Frames fds.Range(func(key, value any) bool { frames = append(frames, value.(*runtime.Frames)) fds.Delete(key) return true }) return frames } func callersFrames(skip int) *runtime.Frames { c := make([]uintptr, 32) // Skip runtime.Callers and this function. i := runtime.Callers(skip+2, c) if i == 0 { return nil } return runtime.CallersFrames(c) } // formatFrames formats a runtime.Frames as a human-readable string. func formatFrames(fs *runtime.Frames) string { var b bytes.Buffer for { f, more := fs.Next() b.WriteString(fmt.Sprintf("\t%s+%#x\n\t\t%s:%d\n", f.Function, f.PC-f.Entry, f.File, f.Line)) if !more { break } } return b.String() } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/testmain/main.go000066400000000000000000000020131520243672000261460ustar00rootroot00000000000000package testmain import ( "flag" "fmt" "os" "sync" "github.com/cilium/ebpf/internal/platform" ) type testingM interface { Run() int } // Run m with various debug aids enabled. // // The function calls [os.Exit] and does not return. func Run(m testingM) { const traceLogFlag = "trace-log" var ts *traceSession if platform.IsWindows { traceLog := flag.Bool(traceLogFlag, false, "Output a trace of eBPF runtime activity") flag.Parse() if *traceLog { var err error ts, err = newTraceSession() if err != nil { fmt.Fprintln(os.Stderr, "Disabling trace logging:", err) } } } defer ts.Close() fds = new(sync.Map) ret := m.Run() for _, f := range flushFrames() { onLeakFD(f) } if foundLeak.Load() { ret = 99 } if err := ts.Dump(os.Stderr); err != nil { fmt.Fprintln(os.Stderr, "Error while dumping trace log:", err) ret = 99 } if platform.IsWindows && ret != 0 && ts == nil { fmt.Fprintf(os.Stderr, "Consider enabling trace logging with -%s\n", traceLogFlag) } os.Exit(ret) } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/testmain/testdata/000077500000000000000000000000001520243672000265105ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/testmain/testdata/trace.xml.gz000066400000000000000000000244441520243672000307570ustar00rootroot00000000000000jgtrace.xml][sF}þ1% YĎ+v2SIF5$ؓn^$JHJpK&.ݍ[Wr1?xUl}8wbq|K~1s1Φxpzq"N9ɋϱK// WfϓQ>;d67T gD(#DbFYn8>:)cw:9%Nl>^W'fq{w>tQ0őQMg){e?|tT>?1#L1ooA;-!Y~>X=Ľл +K>.qv> JdG~q8$5 1CN.)s~r/KlyW W'˯[~d!ہO.ɿӣ#ɭA<>5JIu#0Sn c>a\yŁ-QˑǧsXB5Rm7~+P$[¿|&yb l c)۞|&E~Y핿_ f-wb- m/O{Wy>*2e^ç|淏Kj OO̤!ʒc۞?Y/x7O0[⸬>|i^|廿^{]f;5sq}\Gcwf˘׻?旎.]\ekujv+ZyY00uRZ[B-|ǧE: us=Ii,_=n_>/OWf<Ygŀ@=#Ƣc;Ny_/u,7m*̅WZF0qwar*Q]fJgli&ݾPJyR) OSr`T]x-O'l;g'|zzzzzz=z="#cl١TēR N?JT;P!Ӽ< W*q}|?^i\6ie?j>'L+ LΔtX |$Z"r8g6uX (xr؅Q%a'^ )<#yK <<+ 7;[ὋT@6Iܗ@y"M>Ė.@%7+/?)B'?o-7OBd }y@CV l!2uL$P]*6!B( DYiDQ@~%YnPYPkFf2uLIa41@ &Fq, ߗ2u#*k@O޳غIh51JxWl:r4m^<@q+ƜaOǢٰ7p5S@Cn'u2 w$#]Z9]?\ oU\]n8\M&ĽYvw1:_4Ȼ>ǽ=4[i@wQ4H/a_4dFe|zl#3~>#ӷ=[_ s?t1N.@Srrep}75Gj-vu>h⨬9;m !D*#[,#-^M$ ~_BRS]-Qm ]I4 V+<ը;HoY+Km(\=*Ol>ί] .W1Mg̮.~"76kv:6d1A1TRL',Tpbryk~6`@wEjY*$5 ^ ׶$J^m[/./ J[XbeG@~2JjP4 SQ/2xD;o-y}5y~ᢠ0_7xp6ZLg^._$S 4vc*`<^mک ,7ݦYÏ??zk ;R2n?.g~,.&50ktI"Lr2qHs%ݮ]'F 2̩Rv:Cce(:V &t tnGfpi8Vrg*:H{RU+_`rmUHT>Aʢik-MFQ7,HT$ua6P: B '?_l2NC3Jtcht5*C*n2*m'GSsmbϋ2EtI,L&fR1>v|;:h`O#p8Jp̶1> MbHehHc`5Fa T-^E{F3ח2 hmplh%{:͆{GNRaJ3@QʆBD^p):D ezC l2RIK6mHb:Dm%>]Ry800KaOtD0i&ܯd{qQc}I:UFC7İM `@1LPV9q8m ;5 MMHkS-uZQ4 h6tj8,CVtj<(:6I-ѩSL:`a)4Щ˹ 4ǡSK"ᜪBVt bJ Х q]f要N`fv{Ftj}8vt@&=wvt6ݳ54H)RWqH!!H9]MO%iT`[(3_ sߌ1o'.veRn('?u| 7Y^)f=5ttˌt_wn}Enb:|+f b08#G0!f`vWZтɹRFH#kl݉wg|]DJǫw\ڥ3ڧ~7z>N#LX*%dZBeZTYZzBx3=|g!ü\yj$ţ=J |6~]tS>O,vIK\| դLGT! qYH׸3}wNL8naUV5^|Fӧ'}!79ڈ{ "Yl{-_a"Mc8fZ'xF*HL1fNcVQ M]hB BBW^h Zބxj ,JyVhFM74 -ߢR-[vB BBW^h P ޅ#@hAhYh ~ygZ5tCh=- ,h-AQg ,&TDfc&>9xhu}u0jh;{njĂ~8MMԏ]r{OSfU*8Kr9sk|d@ᅕ.$nZZw3~B,UUa՘@ЪDB ZZmZZ4O%QeưHsakLd1!uUhU ^8jaCLRRyقCh=Fٴ<2f%ДVDeJSgBI΢:t$94X`W4gmz}D_8o9fwFNs/cH;'=S>Ԑ9 -GlϥGXg>0D9gIUpgv۝glr-/p|K#ozf~zF1XjQ{M 8yj=8D=|{8&B;5FKޒ9V39`m-ϋf58rLuD0ftޅ4VY-y#4Ms~w3aNksw)®1JMC-g]7?(ΠE3A~ (NMmBso<8WvͰKtJndOxOF' c wpw\ֿ~'٤Af" B6W ٫>,f'(!b+ހQ/FJ[%'2O/: jtϬIZLԫ443t51KEguZ^;bt3z/|߫=n_IT9a+OeԦFN#9>nqGB,pFonF꾕ta|QJ1|.`F#pɳhlC)Ę0 0C2N9ݩMIy,KpIϺsyV󖐷c"6:|#]BbxKL؅ēzKr9Jge I|/$O2KwBuݜL4Kz}ihU^}+E(rhWI^:k/R5B P\] CuFQ9]'<nsz4.^nugrxD86U 0O0,:|>$ؠ>fP=DyD)[hPEcs Sk pS7;=qB.]i<#cw"R*) NC$AH2τ;7=mY yVWb".-Zpb/F]Z !z!V4gC5 0<~j|K0-@w]*SI=JhIbzu_gǍƣ#C 7ȨQBrԄ!Lbnɍch':+c }:g.C1Xy4zib90Y8^#.AU-6pQjNء9F$7sA\ΓTB2 f~+})5)= HB za/vz-|$:(֔eVD@_{zM bNoGZSf+ZD@\J|uo@n#( cJ]v4gzڸ'hTو"C] uM<\:EfOҸ%mHK*VC'4D7ɥSL"b5aiӻ^)XeL(i]FqCʆx׾`2B3X$<1&`(wuVR0Fgu~s<͢nwȢ>j-"4}pA'^k؂ ^P;v.ew#R%5э#ɷ hgk_R4n`_AтD|J!'Q$ѣmNSڒ6J\)t>qЃe8 IXf|W}'Jn8kT8s b``/@g4Dڌt^L' Gz0:"0GӣjQUP+[ >6MvF4{Oa!D: Js>{o^)"|kՅZ߹h\ $QVGU־.>/gnܫ@4'gȚOG"$d*&Uo2]}h7 TGfx|`ð\ CꜭS$=> $,2J A9UwS!7i[{7ɪtza_.SW~K!)SI~?گsWJv[$z(ksw ң[{O@(S>̺(=,ϙ$ك@P^PE &3H2[$u+D% x/ʸ?c@aT+^ J"dzrX9$tё L B  #t#tъ^,/3 D5mj5:R2pe}4_J 7.>]*pF!X M`TהRi[3;@*@(:ߊb @i}ݱ8Hu;IzƢFENJm=J§Y\/5E-^Br6:R^lm &.X쑻TlH2T"jd,ٗKMal5OFP&i5I̡b EVsWՠho0OWw z|-x[MAy+^Z;nQy$N_rF#P`XTٔEpݿWs\TiQB5wdZGk-/૳rj+sQ\`j{-Lks)>͗.6_Dۇp+1IK~YYzkc]u =f<ɼU^{ph)MZWO^*RVOf1"[Tlexqx]c6xٮ6}0ݝTaZM^ZGkҀ"i9J kq$kC+2zxyR8ہԇrtVh ޢgJDȡrӾYZu1dі"OmIceaUS|l /Q_xe-sx`ay!F|s-s\ތ{vRMcdޗ/!i S OUdD1!&Z(:'P {&o)ܓ$ o9 ;^o[B0_l1Z ߴJ*Sj L&T40$i~5sfFh b[g)8|Ǫ "L|TfI4`nz^Mkcɝ Ҟ(mT*@ҡ`( 1£ Ldr$rlE2>TQ3@VS@k ox5q. 5R5 wiݮLĜ 2 Ғ:%T>g ӽ|8 $G4ExS A m-xHX8bNgw-1pPA[꺵b n˪rq൪;lE[[9( $sW0)s1Cdo;:ӊ@ tŏFh s0JXW8&m<2MQȋӇ]:̺ , uGf8벷h< - 'FCȯZ|h jP[D.uJ1DKQP3 g(ǝ.!8ssbWHTwԑo9d*>?qsM%[,5!&.is9Y+} K[/`۵xtro,}o繟*Y/kSEE"oioiy,5L󆪀?+ƿ3golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/testmain/windows.go000066400000000000000000000110271520243672000267210ustar00rootroot00000000000000package testmain import ( "encoding/xml" "fmt" "io" "os" "os/exec" "path/filepath" "slices" "strconv" "strings" "text/tabwriter" ) type tracelogKeywords uint64 // Know tracelog keywords. // // See https://github.com/microsoft/ebpf-for-windows/blob/main/libs/shared/ebpf_tracelog.h var allKeywords = []string{ "entry-exit", "base", "error", "epoch", "core", "link", "map", "program", "api", "printk", "native", } func (kw *tracelogKeywords) UnmarshalText(text []byte) error { decoded, err := strconv.ParseUint(string(text), 0, 64) if err != nil { return fmt.Errorf("foo: %w", err) } *kw = tracelogKeywords(decoded) return nil } func (kw tracelogKeywords) decode() []string { var keywords []string for _, keyword := range allKeywords { if kw&1 > 0 { keywords = append(keywords, keyword) } kw >>= 1 } if kw > 0 { keywords = append(keywords, fmt.Sprintf("0x%x", kw)) } return keywords } type traceSession struct { session string } // newTraceSession starts a trace log for eBPF for Windows related events. // // * https://github.com/microsoft/ebpf-for-windows/blob/main/docs/GettingStarted.md#using-tracing // * https://devblogs.microsoft.com/performance-diagnostics/controlling-the-event-session-name-with-the-instance-name/ and func newTraceSession() (*traceSession, error) { def := filepath.Join(os.Getenv("ProgramFiles"), "ebpf-for-windows\\ebpfforwindows.wprp") if _, err := os.Stat(def); err != nil { return nil, err } session := fmt.Sprintf("epbf-go-%d", os.Getpid()) wpr := exec.Command("wpr.exe", "-start", def, "-filemode", "-instancename", session) wpr.Stderr = os.Stderr if err := wpr.Run(); err != nil { return nil, err } return &traceSession{session}, nil } func (ts *traceSession) Close() error { if ts == nil { return nil } return ts.stop(os.DevNull) } func (ts *traceSession) stop(file string) error { if ts.session == "" { return nil } wpr := exec.Command("wpr.exe", "-stop", file, "-instancename", ts.session) if err := wpr.Run(); err != nil { return err } ts.session = "" return nil } func (ts *traceSession) Dump(w io.Writer) error { if ts == nil { return nil } path, err := os.MkdirTemp("", "ebpf-go-trace") if err != nil { return err } defer os.RemoveAll(path) trace := filepath.Join(path, "trace.etl") if err := ts.stop(trace); err != nil { return fmt.Errorf("write trace: %w", err) } netsh := exec.Command("netsh.exe", "trace", "convert", trace, "dump=XML") if err := netsh.Run(); err != nil { return err } f, err := os.Open(filepath.Join(path, "trace.xml")) if err != nil { return err } defer f.Close() return summariseWPRTrace(f, w) } func summariseWPRTrace(r io.Reader, w io.Writer) error { type nameValue struct { Name string `xml:"Name,attr"` Value string `xml:",chardata"` } type event struct { XMLName xml.Name `xml:"Event"` System struct { Provider struct { Name string `xml:"Name,attr"` } `xml:"Provider"` TimeCreated struct { SystemTime string `xml:"SystemTime,attr"` } `xml:"TimeCreated"` Keywords tracelogKeywords `xml:"Keywords"` Level uint64 `xml:"Level"` } `xml:"System"` EventData struct { Data []nameValue `xml:"Data"` } `xml:"EventData"` RenderingInfo struct { Task string `xml:"Task"` } `xml:"RenderingInfo"` } var events struct { Events []event `xml:"Event"` } err := xml.NewDecoder(r).Decode(&events) if err != nil { return fmt.Errorf("unmarshal trace XML: %w", err) } tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0) for _, event := range events.Events { if !strings.Contains(event.System.Provider.Name, "Ebpf") { continue } flag := " " // See https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-tracelogginglevel#remarks if event.System.Level > 0 && event.System.Level <= 3 { flag = "!" } kw := event.System.Keywords.decode() fmt.Fprintf(tw, "%s\t%s\t", flag, strings.Join(kw, ",")) data := event.EventData.Data slices.SortFunc(data, func(a, b nameValue) int { return strings.Compare(a.Name, b.Name) }) var first string for _, name := range []string{ "Entry", "Message", "ErrorMessage", } { i := slices.IndexFunc(data, func(kv nameValue) bool { return kv.Name == name }) if i == -1 { continue } first = data[i].Value data = slices.Delete(data, i, i+1) break } // NB: This may be empty. fmt.Fprintf(tw, "%s\t", first) for _, data := range data { fmt.Fprintf(tw, "%s=%s\t", data.Name, data.Value) } fmt.Fprintln(tw) } return tw.Flush() } golang-github-cilium-ebpf-0.21.0+ds1/internal/testutils/testmain/windows_test.go000066400000000000000000000006301520243672000277560ustar00rootroot00000000000000package testmain import ( "bytes" "compress/gzip" "os" "testing" "github.com/go-quicktest/qt" ) func TestSummariseWPRTrace(t *testing.T) { f, err := os.Open("testdata/trace.xml.gz") qt.Assert(t, qt.IsNil(err)) defer f.Close() trace, err := gzip.NewReader(f) qt.Assert(t, qt.IsNil(err)) var buf bytes.Buffer qt.Assert(t, qt.IsNil(summariseWPRTrace(trace, &buf))) t.Log("\n", buf.String()) } golang-github-cilium-ebpf-0.21.0+ds1/internal/tracefs/000077500000000000000000000000001520243672000224425ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/tracefs/kprobe.go000066400000000000000000000260421520243672000242570ustar00rootroot00000000000000package tracefs import ( "crypto/rand" "errors" "fmt" "os" "path/filepath" "runtime" "strings" "sync" "syscall" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/unix" ) var ( ErrInvalidInput = errors.New("invalid input") ErrInvalidMaxActive = errors.New("can only set maxactive on kretprobes") ) //go:generate go tool stringer -type=ProbeType -linecomment type ProbeType uint8 const ( Kprobe ProbeType = iota // kprobe Uprobe // uprobe ) func (pt ProbeType) eventsFile() (*os.File, error) { path, err := sanitizeTracefsPath(fmt.Sprintf("%s_events", pt.String())) if err != nil { return nil, err } return os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0666) } type ProbeArgs struct { Type ProbeType Symbol, Group, Path string Offset, RefCtrOffset, Cookie uint64 Pid, RetprobeMaxActive int Ret bool } // RandomGroup generates a pseudorandom string for use as a tracefs group name. // Returns an error when the output string would exceed 63 characters (kernel // limitation), when rand.Read() fails or when prefix contains characters not // allowed by IsValidTraceID. func RandomGroup(prefix string) (string, error) { if !validIdentifier(prefix) { return "", fmt.Errorf("prefix '%s' must be alphanumeric or underscore: %w", prefix, ErrInvalidInput) } b := make([]byte, 8) if _, err := rand.Read(b); err != nil { return "", fmt.Errorf("reading random bytes: %w", err) } group := fmt.Sprintf("%s_%x", prefix, b) if len(group) > 63 { return "", fmt.Errorf("group name '%s' cannot be longer than 63 characters: %w", group, ErrInvalidInput) } return group, nil } // validIdentifier implements the equivalent of a regex match // against "^[a-zA-Z_][0-9a-zA-Z_-]*$". // // Trace event groups, names and kernel symbols must adhere to this set of // characters. Non-empty, first character must not be a number or hyphen, all // characters must be alphanumeric, underscore or hyphen. func validIdentifier(s string) bool { if len(s) < 1 { return false } for i, c := range []byte(s) { switch { case c >= 'a' && c <= 'z': case c >= 'A' && c <= 'Z': case c == '_': case i > 0 && (c == '-' || c >= '0' && c <= '9'): default: return false } } return true } func sanitizeTracefsPath(path ...string) (string, error) { base, err := getTracefsPath() if err != nil { return "", err } l := filepath.Join(path...) p := filepath.Join(base, l) if !strings.HasPrefix(p, base) { return "", fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, ErrInvalidInput) } return p, nil } // getTracefsPath will return a correct path to the tracefs mount point. // Since kernel 4.1 tracefs should be mounted by default at /sys/kernel/tracing, // but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted. // The available tracefs paths will depends on distribution choices. var getTracefsPath = sync.OnceValues(func() (string, error) { if !platform.IsLinux { return "", fmt.Errorf("tracefs: %w", internal.ErrNotSupportedOnOS) } for _, p := range []struct { path string fsType int64 }{ {"/sys/kernel/tracing", unix.TRACEFS_MAGIC}, {"/sys/kernel/debug/tracing", unix.TRACEFS_MAGIC}, // RHEL/CentOS {"/sys/kernel/debug/tracing", unix.DEBUGFS_MAGIC}, } { if fsType, err := linux.FSType(p.path); err == nil && fsType == p.fsType { return p.path, nil } } return "", errors.New("neither debugfs nor tracefs are mounted") }) // sanitizeIdentifier replaces every invalid character for the tracefs api with an underscore. // // It is equivalent to calling regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString("_"). func sanitizeIdentifier(s string) string { var skip bool return strings.Map(func(c rune) rune { switch { case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9': skip = false return c case skip: return -1 default: skip = true return '_' } }, s) } // EventID reads a trace event's ID from tracefs given its group and name. // The kernel requires group and name to be alphanumeric or underscore. func EventID(group, name string) (uint64, error) { if !validIdentifier(group) { return 0, fmt.Errorf("invalid tracefs group: %q", group) } if !validIdentifier(name) { return 0, fmt.Errorf("invalid tracefs name: %q", name) } path, err := sanitizeTracefsPath("events", group, name, "id") if err != nil { return 0, err } tid, err := internal.ReadUint64FromFile("%d\n", path) if errors.Is(err, os.ErrNotExist) { return 0, err } if err != nil { return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err) } return tid, nil } func probePrefix(ret bool, maxActive int) string { if ret { if maxActive > 0 { return fmt.Sprintf("r%d", maxActive) } return "r" } return "p" } // Event represents an entry in a tracefs probe events file. type Event struct { typ ProbeType group, name string // event id allocated by the kernel. 0 if the event has already been removed. id uint64 cleanup runtime.Cleanup } // NewEvent creates a new ephemeral trace event. // // Returns os.ErrNotExist if symbol is not a valid // kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist // if a probe with the same group and symbol already exists. Returns an error if // args.RetprobeMaxActive is used on non kprobe types. Returns ErrNotSupported if // the kernel is too old to support kretprobe maxactive. func NewEvent(args ProbeArgs) (*Event, error) { // Before attempting to create a trace event through tracefs, // check if an event with the same group and name already exists. // Kernels 4.x and earlier don't return os.ErrExist on writing a duplicate // entry, so we need to rely on reads for detecting uniqueness. eventName := sanitizeIdentifier(args.Symbol) _, err := EventID(args.Group, eventName) if err == nil { return nil, fmt.Errorf("trace event %s/%s: %w", args.Group, eventName, os.ErrExist) } if errors.Is(err, unix.EINVAL) { return nil, fmt.Errorf("trace event %s/%s: %w (unknown symbol?)", args.Group, eventName, err) } if !errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("checking trace event %s/%s: %w", args.Group, eventName, err) } // Open the kprobe_events file in tracefs. f, err := args.Type.eventsFile() if err != nil { return nil, err } defer f.Close() var pe, token string switch args.Type { case Kprobe: // The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt): // p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe // r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe // -:[GRP/]EVENT : Clear a probe // // Some examples: // r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy // p:ebpf_5678/p_my_kprobe __x64_sys_execve // // Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the // kernel default to NR_CPUS. This is desired in most eBPF cases since // subsampling or rate limiting logic can be more accurately implemented in // the eBPF program itself. // See Documentation/kprobes.txt for more details. if args.RetprobeMaxActive != 0 && !args.Ret { return nil, ErrInvalidMaxActive } token = KprobeToken(args) pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.Ret, args.RetprobeMaxActive), args.Group, eventName, token) case Uprobe: // The uprobe_events syntax is as follows: // p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe // r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe // -:[GRP/]EVENT : Clear a probe // // Some examples: // r:ebpf_1234/readline /bin/bash:0x12345 // p:ebpf_5678/main_mySymbol /bin/mybin:0x12345(0x123) // // See Documentation/trace/uprobetracer.txt for more details. if args.RetprobeMaxActive != 0 { return nil, ErrInvalidMaxActive } token = UprobeToken(args) pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.Ret, 0), args.Group, eventName, token) } _, err = f.WriteString(pe) // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // when trying to create a retprobe for a missing symbol. if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("token %s: not found: %w", token, err) } // Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved // to an invalid insn boundary. The exact conditions that trigger this error are // arch specific however. if errors.Is(err, syscall.EILSEQ) { return nil, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist) } // ERANGE is returned when the `SYM[+offs]` token is too big and cannot // be resolved. if errors.Is(err, syscall.ERANGE) { return nil, fmt.Errorf("token %s: offset too big: %w", token, os.ErrNotExist) } if err != nil { return nil, fmt.Errorf("token %s: writing '%s': %w", token, pe, err) } // Get the newly-created trace event's id. tid, err := EventID(args.Group, eventName) if args.RetprobeMaxActive != 0 && errors.Is(err, os.ErrNotExist) { // Kernels < 4.12 don't support maxactive and therefore auto generate // group and event names from the symbol and offset. The symbol is used // without any sanitization. // See https://elixir.bootlin.com/linux/v4.10/source/kernel/trace/trace_kprobe.c#L712 event := fmt.Sprintf("kprobes/r_%s_%d", args.Symbol, args.Offset) if err := removeEvent(args.Type, event); err != nil { return nil, fmt.Errorf("failed to remove spurious maxactive event: %s", err) } return nil, &internal.UnsupportedFeatureError{ MinimumVersion: internal.Version{4, 12}, Name: "trace event with non-default maxactive", } } if err != nil { return nil, fmt.Errorf("get trace event id: %w", err) } evt := &Event{typ: args.Type, group: args.Group, name: eventName, id: tid} evt.cleanup = runtime.AddCleanup(evt, func(*byte) { _ = removeEvent(args.Type, fmt.Sprintf("%s/%s", args.Group, eventName)) }, nil) return evt, nil } // Close removes the event from tracefs. // // Returns os.ErrClosed if the event has already been closed before. func (evt *Event) Close() error { if evt.id == 0 { return os.ErrClosed } evt.id = 0 evt.cleanup.Stop() pe := fmt.Sprintf("%s/%s", evt.group, evt.name) return removeEvent(evt.typ, pe) } func removeEvent(typ ProbeType, pe string) error { f, err := typ.eventsFile() if err != nil { return err } defer f.Close() // See [k,u]probe_events syntax above. The probe type does not need to be specified // for removals. if _, err = f.WriteString("-:" + pe); err != nil { return fmt.Errorf("remove event %q from %s: %w", pe, f.Name(), err) } return nil } // ID returns the tracefs ID associated with the event. func (evt *Event) ID() uint64 { return evt.id } // Group returns the tracefs group used by the event. func (evt *Event) Group() string { return evt.group } // KprobeToken creates the SYM[+offs] token for the tracefs api. func KprobeToken(args ProbeArgs) string { po := args.Symbol if args.Offset != 0 { po += fmt.Sprintf("+%#x", args.Offset) } return po } golang-github-cilium-ebpf-0.21.0+ds1/internal/tracefs/kprobe_test.go000066400000000000000000000041131520243672000253110ustar00rootroot00000000000000package tracefs import ( "fmt" "os" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/testutils" ) // Global symbol, present on all tested kernels. const ksym = "vprintk" func TestKprobeTraceFSGroup(t *testing.T) { // Expect _<16 random hex chars>. g, err := RandomGroup("ebpftest") qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Matches(g, `ebpftest_[a-f0-9]{16}`)) // Expect error when the generator's output exceeds 63 characters. p := make([]byte, 47) // 63 - 17 (length of the random suffix and underscore) + 1 for i := range p { p[i] = byte('a') } _, err = RandomGroup(string(p)) qt.Assert(t, qt.Not(qt.IsNil(err))) // Reject non-alphanumeric characters. _, err = RandomGroup("/") qt.Assert(t, qt.Not(qt.IsNil(err))) } func TestKprobeToken(t *testing.T) { tests := []struct { args ProbeArgs expected string }{ {ProbeArgs{Symbol: "symbol"}, "symbol"}, {ProbeArgs{Symbol: "symbol", Offset: 1}, "symbol+0x1"}, {ProbeArgs{Symbol: "symbol", Offset: 65535}, "symbol+0xffff"}, {ProbeArgs{Symbol: "symbol", Offset: 65536}, "symbol+0x10000"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { po := KprobeToken(tt.args) if tt.expected != po { t.Errorf("Expected symbol+offset to be '%s', got '%s'", tt.expected, po) } }) } } func TestNewEvent(t *testing.T) { for _, args := range []ProbeArgs{ {Type: Kprobe, Symbol: ksym}, {Type: Kprobe, Symbol: ksym, Ret: true}, {Type: Uprobe, Path: "/bin/bash", Symbol: "main"}, {Type: Uprobe, Path: "/bin/bash", Symbol: "main", Ret: true}, } { name := fmt.Sprintf("%s ret=%v", args.Type, args.Ret) t.Run(name, func(t *testing.T) { args.Group, _ = RandomGroup("ebpftest") evt, err := NewEvent(args) testutils.SkipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) defer evt.Close() _, err = NewEvent(args) qt.Assert(t, qt.ErrorIs(err, os.ErrExist), qt.Commentf("expected consecutive event creation to contain os.ErrExist")) qt.Assert(t, qt.IsNil(evt.Close())) qt.Assert(t, qt.ErrorIs(evt.Close(), os.ErrClosed)) }) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/tracefs/perf_event_test.go000066400000000000000000000040761520243672000261740ustar00rootroot00000000000000package tracefs import ( "errors" "fmt" "os" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/testutils" ) func TestEventID(t *testing.T) { eid, err := EventID("syscalls", "sys_enter_mmap") testutils.SkipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Not(qt.Equals(eid, 0))) } func TestSanitizePath(t *testing.T) { _, err := sanitizeTracefsPath("../escaped") testutils.SkipIfNotSupportedOnOS(t, err) if !errors.Is(err, ErrInvalidInput) { t.Errorf("expected error %s, got: %s", ErrInvalidInput, err) } _, err = sanitizeTracefsPath("./not/escaped") if err != nil { t.Errorf("expected no error, got: %s", err) } } func TestValidIdentifier(t *testing.T) { tests := []struct { name string in string fail bool }{ {"empty string", "", true}, {"leading number", "1test", true}, {"underscore first", "__x64_syscall", false}, {"contains number", "bpf_trace_run1", false}, {"underscore", "_", false}, {"leading dash", "-EINVAL", true}, {"contains number", "all0wed", false}, {"contains dash", "trace-group", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { exp := "pass" if tt.fail { exp = "fail" } if validIdentifier(tt.in) == tt.fail { t.Errorf("expected string '%s' to %s valid ID check", tt.in, exp) } }) } } func TestSanitizeIdentifier(t *testing.T) { tests := []struct { symbol string expected string }{ {"readline", "readline"}, {"main.Func123", "main_Func123"}, {"a.....a", "a_a"}, {"./;'{}[]a", "_a"}, {"***xx**xx###", "_xx_xx_"}, {`@P#r$i%v^3*+t)i&k++--`, "_P_r_i_v_3_t_i_k_"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { sanitized := sanitizeIdentifier(tt.symbol) if tt.expected != sanitized { t.Errorf("Expected sanitized symbol to be '%s', got '%s'", tt.expected, sanitized) } }) } } func TestGetTracefsPath(t *testing.T) { path, err := getTracefsPath() testutils.SkipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) _, err = os.Stat(path) qt.Assert(t, qt.IsNil(err)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/tracefs/probetype_string.go000066400000000000000000000012141520243672000263660ustar00rootroot00000000000000// Code generated by "stringer -type=ProbeType -linecomment"; DO NOT EDIT. package tracefs import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Kprobe-0] _ = x[Uprobe-1] } const _ProbeType_name = "kprobeuprobe" var _ProbeType_index = [...]uint8{0, 6, 12} func (i ProbeType) String() string { idx := int(i) - 0 if i < 0 || idx >= len(_ProbeType_index)-1 { return "ProbeType(" + strconv.FormatInt(int64(i), 10) + ")" } return _ProbeType_name[_ProbeType_index[idx]:_ProbeType_index[idx+1]] } golang-github-cilium-ebpf-0.21.0+ds1/internal/tracefs/uprobe.go000066400000000000000000000006761520243672000242760ustar00rootroot00000000000000package tracefs import "fmt" // UprobeToken creates the PATH:OFFSET(REF_CTR_OFFSET) token for the tracefs api. func UprobeToken(args ProbeArgs) string { po := fmt.Sprintf("%s:%#x", args.Path, args.Offset) if args.RefCtrOffset != 0 { // This is not documented in Documentation/trace/uprobetracer.txt. // elixir.bootlin.com/linux/v5.15-rc7/source/kernel/trace/trace.c#L5564 po += fmt.Sprintf("(%#x)", args.RefCtrOffset) } return po } golang-github-cilium-ebpf-0.21.0+ds1/internal/tracefs/uprobe_test.go000066400000000000000000000014351520243672000253270ustar00rootroot00000000000000package tracefs import ( "fmt" "testing" ) func TestUprobeToken(t *testing.T) { tests := []struct { args ProbeArgs expected string }{ {ProbeArgs{Path: "/bin/bash"}, "/bin/bash:0x0"}, {ProbeArgs{Path: "/bin/bash", Offset: 1}, "/bin/bash:0x1"}, {ProbeArgs{Path: "/bin/bash", Offset: 65535}, "/bin/bash:0xffff"}, {ProbeArgs{Path: "/bin/bash", Offset: 65536}, "/bin/bash:0x10000"}, {ProbeArgs{Path: "/bin/bash", Offset: 1, RefCtrOffset: 1}, "/bin/bash:0x1(0x1)"}, {ProbeArgs{Path: "/bin/bash", Offset: 1, RefCtrOffset: 65535}, "/bin/bash:0x1(0xffff)"}, } for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { po := UprobeToken(tt.args) if tt.expected != po { t.Errorf("Expected path:offset to be '%s', got '%s'", tt.expected, po) } }) } } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/000077500000000000000000000000001520243672000217765ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/doc.go000066400000000000000000000007651520243672000231020ustar00rootroot00000000000000// Package unix re-exports Linux specific parts of golang.org/x/sys/unix. // // It avoids breaking compilation on other OS by providing stubs as follows: // - Invoking a function always returns an error. // - Errnos have distinct, non-zero values. // - Constants have distinct but meaningless values. // - Types use the same names for members, but may or may not follow the // Linux layout. package unix // Note: please don't add any custom API to this package. Use internal/sys instead. golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/errno_linux.go000066400000000000000000000010661520243672000246740ustar00rootroot00000000000000package unix import ( "syscall" linux "golang.org/x/sys/unix" ) type Errno = syscall.Errno const ( E2BIG = linux.E2BIG EACCES = linux.EACCES EAGAIN = linux.EAGAIN EBADF = linux.EBADF EEXIST = linux.EEXIST EFAULT = linux.EFAULT EILSEQ = linux.EILSEQ EINTR = linux.EINTR EINVAL = linux.EINVAL ENODEV = linux.ENODEV ENOENT = linux.ENOENT ENOSPC = linux.ENOSPC EOPNOTSUPP = linux.EOPNOTSUPP EPERM = linux.EPERM EPOLLIN = linux.EPOLLIN ESRCH = linux.ESRCH ESTALE = linux.ESTALE ) golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/errno_linux_test.go000066400000000000000000000003401520243672000257250ustar00rootroot00000000000000package unix import ( "testing" "github.com/go-quicktest/qt" "golang.org/x/sys/unix" ) func TestErrnoIsUnix(t *testing.T) { qt.Assert(t, qt.ErrorIs(EPERM, unix.EPERM)) qt.Assert(t, qt.ErrorIs(ENOENT, unix.ENOENT)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/errno_other.go000066400000000000000000000004551520243672000246570ustar00rootroot00000000000000//go:build !linux && !windows package unix import "syscall" type Errno = syscall.Errno // Errnos are distinct and non-zero. const ( E2BIG Errno = iota + 1 EACCES EAGAIN EBADF EEXIST EFAULT EILSEQ EINTR EINVAL ENODEV ENOENT ENOSPC ENOTSUP ENOTSUPP EOPNOTSUPP EPERM ESRCH ESTALE ) golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/errno_string_windows.go000066400000000000000000000030121520243672000266060ustar00rootroot00000000000000// Code generated by "stringer -type=Errno -tags=windows -output=errno_string_windows.go"; DO NOT EDIT. package unix import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[EPERM-1] _ = x[ENOENT-2] _ = x[ESRCH-3] _ = x[EINTR-4] _ = x[E2BIG-7] _ = x[EBADF-9] _ = x[EAGAIN-11] _ = x[EACCES-13] _ = x[EFAULT-14] _ = x[EEXIST-17] _ = x[ENODEV-19] _ = x[EINVAL-22] _ = x[ENOSPC-28] _ = x[EILSEQ-42] _ = x[ENOTSUP-129] _ = x[EOPNOTSUPP-130] _ = x[ENOTSUPP-536870912] _ = x[ESTALE-536870913] } const _Errno_name = "EPERMENOENTESRCHEINTRE2BIGEBADFEAGAINEACCESEFAULTEEXISTENODEVEINVALENOSPCEILSEQENOTSUPEOPNOTSUPPENOTSUPPESTALE" var _Errno_map = map[Errno]string{ 1: _Errno_name[0:5], 2: _Errno_name[5:11], 3: _Errno_name[11:16], 4: _Errno_name[16:21], 7: _Errno_name[21:26], 9: _Errno_name[26:31], 11: _Errno_name[31:37], 13: _Errno_name[37:43], 14: _Errno_name[43:49], 17: _Errno_name[49:55], 19: _Errno_name[55:61], 22: _Errno_name[61:67], 28: _Errno_name[67:73], 42: _Errno_name[73:79], 129: _Errno_name[79:86], 130: _Errno_name[86:96], 536870912: _Errno_name[96:104], 536870913: _Errno_name[104:110], } func (i Errno) String() string { if str, ok := _Errno_map[i]; ok { return str } return "Errno(" + strconv.FormatInt(int64(i), 10) + ")" } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/errno_test.go000066400000000000000000000002351520243672000245110ustar00rootroot00000000000000package unix import ( "os" "testing" "github.com/go-quicktest/qt" ) func TestErrno(t *testing.T) { qt.Assert(t, qt.ErrorIs(ENOENT, os.ErrNotExist)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/errno_windows.go000066400000000000000000000034771520243672000252370ustar00rootroot00000000000000package unix // The code in this file is derived from syscall_unix.go in the Go source code, // licensed under the MIT license. import ( "errors" "os" "syscall" ) //go:generate go tool stringer -type=Errno -tags=windows -output=errno_string_windows.go // Windows specific constants for Unix errnos. // // The values do not always match Linux, for example EILSEQ and EOPNOTSUPP. // // See https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170 const ( EPERM Errno = 1 ENOENT Errno = 2 ESRCH Errno = 3 EINTR Errno = 4 E2BIG Errno = 7 EBADF Errno = 9 EAGAIN Errno = 11 EACCES Errno = 13 EFAULT Errno = 14 EEXIST Errno = 17 ENODEV Errno = 19 EINVAL Errno = 22 ENFILE Errno = 23 EMFILE Errno = 24 ENOSPC Errno = 28 ENOSYS Errno = 40 ENOTEMPTY Errno = 41 EILSEQ Errno = 42 ENOTSUP Errno = 129 EOPNOTSUPP Errno = 130 EOTHER Errno = 131 ETIMEDOUT Errno = 138 EWOULDBLOCK Errno = 140 ) // These constants do not exist on Windows and therefore have a non-zero // dummy value. const ( ENOTSUPP Errno = Errno(syscall.APPLICATION_ERROR) + iota ESTALE ) // Errno is a Windows compatibility shim for Unix errnos. type Errno uintptr func (e Errno) Error() string { return e.String() } func (e Errno) Is(target error) bool { switch target { case os.ErrPermission: return e == EACCES || e == EPERM case os.ErrExist: return e == EEXIST || e == ENOTEMPTY case os.ErrNotExist: return e == ENOENT case errors.ErrUnsupported: return e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP } return false } func (e Errno) Temporary() bool { return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout() } func (e Errno) Timeout() bool { return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/error.go000066400000000000000000000007711520243672000234630ustar00rootroot00000000000000package unix import ( "fmt" "runtime" "strings" "github.com/cilium/ebpf/internal" ) // errNonLinux returns an error which wraps [internal.ErrNotSupportedOnOS] and // includes the name of the calling function. func errNonLinux() error { name := "unknown" pc, _, _, ok := runtime.Caller(1) if ok { name = runtime.FuncForPC(pc).Name() if pos := strings.LastIndexByte(name, '.'); pos != -1 { name = name[pos+1:] } } return fmt.Errorf("unix: %s: %w", name, internal.ErrNotSupportedOnOS) } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/error_test.go000066400000000000000000000004321520243672000245140ustar00rootroot00000000000000package unix import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal" ) func TestErrNonLinux(t *testing.T) { err := errNonLinux() qt.Assert(t, qt.StringContains(err.Error(), t.Name())) qt.Assert(t, qt.ErrorIs(err, internal.ErrNotSupportedOnOS)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/strings_other.go000066400000000000000000000004071520243672000252200ustar00rootroot00000000000000//go:build !linux && !windows package unix func BytePtrFromString(s string) (*byte, error) { return nil, errNonLinux() } func ByteSliceToString(s []byte) string { return "" } func ByteSliceFromString(s string) ([]byte, error) { return nil, errNonLinux() } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/strings_windows.go000066400000000000000000000006141520243672000255710ustar00rootroot00000000000000package unix import ( "syscall" "golang.org/x/sys/windows" ) func BytePtrFromString(s string) (*byte, error) { p, err := windows.BytePtrFromString(s) if err == syscall.EINVAL { err = EINVAL } return p, err } func ByteSliceToString(s []byte) string { return windows.ByteSliceToString(s) } func ByteSliceFromString(s string) ([]byte, error) { return windows.ByteSliceFromString(s) } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/types_linux.go000066400000000000000000000162711520243672000247170ustar00rootroot00000000000000//go:build linux package unix import ( "syscall" "unsafe" linux "golang.org/x/sys/unix" ) const ( BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE BPF_F_RDONLY = linux.BPF_F_RDONLY BPF_F_WRONLY = linux.BPF_F_WRONLY BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE BPF_F_XDP_HAS_FRAGS = linux.BPF_F_XDP_HAS_FRAGS BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP BPF_F_KPROBE_MULTI_RETURN = linux.BPF_F_KPROBE_MULTI_RETURN BPF_F_UPROBE_MULTI_RETURN = linux.BPF_F_UPROBE_MULTI_RETURN BPF_F_LOCK = linux.BPF_F_LOCK BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN BPF_TAG_SIZE = linux.BPF_TAG_SIZE BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ SYS_BPF = linux.SYS_BPF F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC O_RDONLY = linux.O_RDONLY O_CLOEXEC = linux.O_CLOEXEC O_NONBLOCK = linux.O_NONBLOCK PROT_NONE = linux.PROT_NONE PROT_READ = linux.PROT_READ PROT_WRITE = linux.PROT_WRITE MAP_ANON = linux.MAP_ANON MAP_SHARED = linux.MAP_SHARED MAP_FIXED = linux.MAP_FIXED MAP_PRIVATE = linux.MAP_PRIVATE PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1 PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF PerfBitWatermark = linux.PerfBitWatermark PerfBitWriteBackward = linux.PerfBitWriteBackward PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC RLIM_INFINITY = linux.RLIM_INFINITY RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME PERF_RECORD_LOST = linux.PERF_RECORD_LOST PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE AT_FDCWD = linux.AT_FDCWD RENAME_NOREPLACE = linux.RENAME_NOREPLACE SO_ATTACH_BPF = linux.SO_ATTACH_BPF SO_DETACH_BPF = linux.SO_DETACH_BPF SOL_SOCKET = linux.SOL_SOCKET SIGPROF = linux.SIGPROF SIGUSR1 = linux.SIGUSR1 SIG_BLOCK = linux.SIG_BLOCK SIG_UNBLOCK = linux.SIG_UNBLOCK BPF_FS_MAGIC = linux.BPF_FS_MAGIC TRACEFS_MAGIC = linux.TRACEFS_MAGIC DEBUGFS_MAGIC = linux.DEBUGFS_MAGIC BPF_RB_NO_WAKEUP = linux.BPF_RB_NO_WAKEUP BPF_RB_FORCE_WAKEUP = linux.BPF_RB_FORCE_WAKEUP AF_UNSPEC = linux.AF_UNSPEC IFF_UP = linux.IFF_UP CLONE_NEWNET = linux.CLONE_NEWNET LINUX_CAPABILITY_VERSION_3 = linux.LINUX_CAPABILITY_VERSION_3 ) type Statfs_t = linux.Statfs_t type Stat_t = linux.Stat_t type Rlimit = linux.Rlimit type Signal = linux.Signal type Sigset_t = linux.Sigset_t type PerfEventMmapPage = linux.PerfEventMmapPage type EpollEvent = linux.EpollEvent type PerfEventAttr = linux.PerfEventAttr type Utsname = linux.Utsname type CPUSet = linux.CPUSet type CapUserData = linux.CapUserData type CapUserHeader = linux.CapUserHeader func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return linux.Syscall(trap, a1, a2, a3) } func PthreadSigmask(how int, set, oldset *Sigset_t) error { return linux.PthreadSigmask(how, set, oldset) } func FcntlInt(fd uintptr, cmd, arg int) (int, error) { return linux.FcntlInt(fd, cmd, arg) } func IoctlSetInt(fd int, req uint, value int) error { return linux.IoctlSetInt(fd, req, value) } func Statfs(path string, buf *Statfs_t) (err error) { return linux.Statfs(path, buf) } func Close(fd int) (err error) { return linux.Close(fd) } func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { return linux.EpollWait(epfd, events, msec) } func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { return linux.EpollCtl(epfd, op, fd, event) } func Eventfd(initval uint, flags int) (fd int, err error) { return linux.Eventfd(initval, flags) } func Write(fd int, p []byte) (n int, err error) { return linux.Write(fd, p) } func EpollCreate1(flag int) (fd int, err error) { return linux.EpollCreate1(flag) } func SetNonblock(fd int, nonblocking bool) (err error) { return linux.SetNonblock(fd, nonblocking) } func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return linux.Mmap(fd, offset, length, prot, flags) } //go:nocheckptr func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) { return linux.MmapPtr(fd, offset, addr, length, prot, flags) } func Munmap(b []byte) (err error) { return linux.Munmap(b) } func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags) } func Uname(buf *Utsname) (err error) { return linux.Uname(buf) } func Getpid() int { return linux.Getpid() } func Gettid() int { return linux.Gettid() } func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { return linux.Tgkill(tgid, tid, sig) } func BytePtrFromString(s string) (*byte, error) { return linux.BytePtrFromString(s) } func ByteSliceToString(s []byte) string { return linux.ByteSliceToString(s) } func ByteSliceFromString(s string) ([]byte, error) { return linux.ByteSliceFromString(s) } func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { return linux.Renameat2(olddirfd, oldpath, newdirfd, newpath, flags) } func Prlimit(pid, resource int, new, old *Rlimit) error { return linux.Prlimit(pid, resource, new, old) } func Open(path string, mode int, perm uint32) (int, error) { return linux.Open(path, mode, perm) } func Fstat(fd int, stat *Stat_t) error { return linux.Fstat(fd, stat) } func SetsockoptInt(fd, level, opt, value int) error { return linux.SetsockoptInt(fd, level, opt, value) } func SchedSetaffinity(pid int, set *CPUSet) error { return linux.SchedSetaffinity(pid, set) } func SchedGetaffinity(pid int, set *CPUSet) error { return linux.SchedGetaffinity(pid, set) } func Auxv() ([][2]uintptr, error) { return linux.Auxv() } func Unshare(flag int) error { return linux.Unshare(flag) } func Setns(fd int, nstype int) error { return linux.Setns(fd, nstype) } func Capget(hdr *CapUserHeader, data *CapUserData) (err error) { return linux.Capget(hdr, data) } func Capset(hdr *CapUserHeader, data *CapUserData) (err error) { return linux.Capset(hdr, data) } golang-github-cilium-ebpf-0.21.0+ds1/internal/unix/types_other.go000066400000000000000000000130161520243672000246730ustar00rootroot00000000000000//go:build !linux package unix import ( "syscall" "unsafe" ) // Constants are distinct to avoid breaking switch statements. const ( BPF_F_NO_PREALLOC = iota BPF_F_NUMA_NODE BPF_F_RDONLY BPF_F_WRONLY BPF_F_RDONLY_PROG BPF_F_WRONLY_PROG BPF_F_SLEEPABLE BPF_F_MMAPABLE BPF_F_INNER_MAP BPF_F_KPROBE_MULTI_RETURN BPF_F_UPROBE_MULTI_RETURN BPF_F_XDP_HAS_FRAGS BPF_OBJ_NAME_LEN BPF_TAG_SIZE BPF_RINGBUF_BUSY_BIT BPF_RINGBUF_DISCARD_BIT BPF_RINGBUF_HDR_SZ SYS_BPF F_DUPFD_CLOEXEC EPOLLIN EPOLL_CTL_ADD EPOLL_CLOEXEC O_CLOEXEC O_NONBLOCK PROT_NONE PROT_READ PROT_WRITE MAP_ANON MAP_SHARED MAP_FIXED MAP_PRIVATE PERF_ATTR_SIZE_VER1 PERF_TYPE_SOFTWARE PERF_TYPE_TRACEPOINT PERF_COUNT_SW_BPF_OUTPUT PERF_EVENT_IOC_DISABLE PERF_EVENT_IOC_ENABLE PERF_EVENT_IOC_SET_BPF PerfBitWatermark PerfBitWriteBackward PERF_SAMPLE_RAW PERF_FLAG_FD_CLOEXEC RLIM_INFINITY RLIMIT_MEMLOCK BPF_STATS_RUN_TIME PERF_RECORD_LOST PERF_RECORD_SAMPLE AT_FDCWD RENAME_NOREPLACE SO_ATTACH_BPF SO_DETACH_BPF SOL_SOCKET SIGPROF SIGUSR1 SIG_BLOCK SIG_UNBLOCK BPF_FS_MAGIC TRACEFS_MAGIC DEBUGFS_MAGIC BPF_RB_NO_WAKEUP BPF_RB_FORCE_WAKEUP BPF_F_LOCK AF_UNSPEC IFF_UP LINUX_CAPABILITY_VERSION_3 ) type Statfs_t struct { Type int64 Bsize int64 Blocks uint64 Bfree uint64 Bavail uint64 Files uint64 Ffree uint64 Fsid [2]int32 Namelen int64 Frsize int64 Flags int64 Spare [4]int64 } type Stat_t struct { Dev uint64 Ino uint64 Nlink uint64 Mode uint32 Uid uint32 Gid uint32 _ int32 Rdev uint64 Size int64 Blksize int64 Blocks int64 } type Rlimit struct { Cur uint64 Max uint64 } type Signal int type Sigset_t struct { Val [4]uint64 } type CapUserHeader struct { Version uint32 Pid int32 } type CapUserData struct { Effective uint32 Permitted uint32 Inheritable uint32 } func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { return 0, 0, ENOTSUP } func PthreadSigmask(how int, set, oldset *Sigset_t) error { return errNonLinux() } func FcntlInt(fd uintptr, cmd, arg int) (int, error) { return -1, errNonLinux() } func IoctlSetInt(fd int, req uint, value int) error { return errNonLinux() } func Statfs(path string, buf *Statfs_t) error { return errNonLinux() } func Close(fd int) (err error) { return errNonLinux() } type EpollEvent struct { Events uint32 Fd int32 Pad int32 } func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { return 0, errNonLinux() } func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { return errNonLinux() } func Eventfd(initval uint, flags int) (fd int, err error) { return 0, errNonLinux() } func Write(fd int, p []byte) (n int, err error) { return 0, errNonLinux() } func EpollCreate1(flag int) (fd int, err error) { return 0, errNonLinux() } type PerfEventMmapPage struct { Version uint32 Compat_version uint32 Lock uint32 Index uint32 Offset int64 Time_enabled uint64 Time_running uint64 Capabilities uint64 Pmc_width uint16 Time_shift uint16 Time_mult uint32 Time_offset uint64 Time_zero uint64 Size uint32 Data_head uint64 Data_tail uint64 Data_offset uint64 Data_size uint64 Aux_head uint64 Aux_tail uint64 Aux_offset uint64 Aux_size uint64 } func SetNonblock(fd int, nonblocking bool) (err error) { return errNonLinux() } func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return []byte{}, errNonLinux() } func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) { return nil, errNonLinux() } func Munmap(b []byte) (err error) { return errNonLinux() } type PerfEventAttr struct { Type uint32 Size uint32 Config uint64 Sample uint64 Sample_type uint64 Read_format uint64 Bits uint64 Wakeup uint32 Bp_type uint32 Ext1 uint64 Ext2 uint64 Branch_sample_type uint64 Sample_regs_user uint64 Sample_stack_user uint32 Clockid int32 Sample_regs_intr uint64 Aux_watermark uint32 Sample_max_stack uint16 } func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { return 0, errNonLinux() } type Utsname struct { Release [65]byte Version [65]byte } func Uname(buf *Utsname) (err error) { return errNonLinux() } func Getpid() int { return -1 } func Gettid() int { return -1 } func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { return errNonLinux() } func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { return errNonLinux() } func Prlimit(pid, resource int, new, old *Rlimit) error { return errNonLinux() } func Open(path string, mode int, perm uint32) (int, error) { return -1, errNonLinux() } func Fstat(fd int, stat *Stat_t) error { return errNonLinux() } func SetsockoptInt(fd, level, opt, value int) error { return errNonLinux() } type CPUSet struct{} func (*CPUSet) Set(int) {} func SchedSetaffinity(pid int, set *CPUSet) error { return errNonLinux() } func SchedGetaffinity(pid int, set *CPUSet) error { return errNonLinux() } func Auxv() ([][2]uintptr, error) { return nil, errNonLinux() } func Capget(hdr *CapUserHeader, data *CapUserData) (err error) { return errNonLinux() } func Capset(hdr *CapUserHeader, data *CapUserData) (err error) { return errNonLinux() } golang-github-cilium-ebpf-0.21.0+ds1/internal/version.go000066400000000000000000000037651520243672000230420ustar00rootroot00000000000000package internal import ( "fmt" ) const ( // Version constant used in ELF binaries indicating that the loader needs to // substitute the eBPF program's version with the value of the kernel's // KERNEL_VERSION compile-time macro. Used for compatibility with BCC, gobpf // and RedSift. MagicKernelVersion = 0xFFFFFFFE ) // A Version in the form Major.Minor.Patch. type Version [3]uint16 // NewVersion creates a version from a string like "Major.Minor.Patch". // // Patch is optional. func NewVersion(ver string) (Version, error) { var major, minor, patch uint16 n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch) if n < 2 { return Version{}, fmt.Errorf("invalid version: %s", ver) } return Version{major, minor, patch}, nil } // NewVersionFromCode creates a version from a LINUX_VERSION_CODE. func NewVersionFromCode(code uint32) Version { return Version{ uint16(uint8(code >> 16)), uint16(uint8(code >> 8)), uint16(uint8(code)), } } func (v Version) String() string { if v[2] == 0 { return fmt.Sprintf("v%d.%d", v[0], v[1]) } return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2]) } // Less returns true if the version is less than another version. func (v Version) Less(other Version) bool { for i, a := range v { if a == other[i] { continue } return a < other[i] } return false } // Unspecified returns true if the version is all zero. func (v Version) Unspecified() bool { return v[0] == 0 && v[1] == 0 && v[2] == 0 } // Kernel implements the kernel's KERNEL_VERSION macro from linux/version.h. // It represents the kernel version and patch level as a single value. func (v Version) Kernel() uint32 { // Kernels 4.4 and 4.9 have their SUBLEVEL clamped to 255 to avoid // overflowing into PATCHLEVEL. // See kernel commit 9b82f13e7ef3 ("kbuild: clamp SUBLEVEL to 255"). s := min(v[2], 255) // Truncate members to uint8 to prevent them from spilling over into // each other when overflowing 8 bits. return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s)) } golang-github-cilium-ebpf-0.21.0+ds1/internal/version_test.go000066400000000000000000000030151520243672000240650ustar00rootroot00000000000000package internal import ( "testing" ) func TestVersion(t *testing.T) { a, err := NewVersion("1.2") if err != nil { t.Fatal(err) } b, err := NewVersion("2.2.1") if err != nil { t.Fatal(err) } if !a.Less(b) { t.Error("A should be less than B") } if b.Less(a) { t.Error("B shouldn't be less than A") } v200 := Version{2, 0, 0} if !a.Less(v200) { t.Error("1.2.1 should not be less than 2.0.0") } if v200.Less(a) { t.Error("2.0.0 should not be less than 1.2.1") } } func TestKernelVersion(t *testing.T) { // Kernels 4.4 and 4.9 have a SUBLEVEL of over 255 and clamp it to 255. // In our implementation, the other version segments are truncated. if v, want := (Version{256, 256, 256}), uint32(255); v.Kernel() != want { t.Errorf("256.256.256 should result in a kernel version of %d, got: %d", want, v.Kernel()) } // Known good version. if v, want := (Version{4, 9, 128}), uint32(264576); v.Kernel() != want { t.Errorf("4.9.1 should result in a kernel version of %d, got: %d", want, v.Kernel()) } } func TestVersionFromCode(t *testing.T) { var tests = []struct { name string code uint32 v Version }{ {"0.0.0", 0, Version{0, 0, 0}}, {"1.0.0", 0x10000, Version{1, 0, 0}}, {"4.4.255", 0x404ff, Version{4, 4, 255}}, {"255.255.255", 0xffffff, Version{255, 255, 255}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v := NewVersionFromCode(tt.code) if v != tt.v { t.Errorf("unexpected version for code '%d'. got: %v, want: %v", tt.code, v, tt.v) } }) } } golang-github-cilium-ebpf-0.21.0+ds1/link/000077500000000000000000000000001520243672000201345ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/link/anchor.go000066400000000000000000000064651520243672000217500ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) const anchorFlags = sys.BPF_F_REPLACE | sys.BPF_F_BEFORE | sys.BPF_F_AFTER | sys.BPF_F_ID | sys.BPF_F_LINK_MPROG // Anchor is a reference to a link or program. // // It is used to describe where an attachment or detachment should take place // for link types which support multiple attachment. type Anchor interface { // anchor returns an fd or ID and a set of flags. // // By default fdOrID is taken to reference a program, but BPF_F_LINK_MPROG // changes this to refer to a link instead. // // BPF_F_BEFORE, BPF_F_AFTER, BPF_F_REPLACE modify where a link or program // is attached. The default behaviour if none of these flags is specified // matches BPF_F_AFTER. anchor() (fdOrID, flags uint32, _ error) } type firstAnchor struct{} func (firstAnchor) anchor() (fdOrID, flags uint32, _ error) { return 0, sys.BPF_F_BEFORE, nil } // Head is the position before all other programs or links. func Head() Anchor { return firstAnchor{} } type lastAnchor struct{} func (lastAnchor) anchor() (fdOrID, flags uint32, _ error) { return 0, sys.BPF_F_AFTER, nil } // Tail is the position after all other programs or links. func Tail() Anchor { return lastAnchor{} } // Before is the position just in front of target. func BeforeLink(target Link) Anchor { return anchor{target, sys.BPF_F_BEFORE} } // After is the position just after target. func AfterLink(target Link) Anchor { return anchor{target, sys.BPF_F_AFTER} } // Before is the position just in front of target. func BeforeLinkByID(target ID) Anchor { return anchor{target, sys.BPF_F_BEFORE} } // After is the position just after target. func AfterLinkByID(target ID) Anchor { return anchor{target, sys.BPF_F_AFTER} } // Before is the position just in front of target. func BeforeProgram(target *ebpf.Program) Anchor { return anchor{target, sys.BPF_F_BEFORE} } // After is the position just after target. func AfterProgram(target *ebpf.Program) Anchor { return anchor{target, sys.BPF_F_AFTER} } // Replace the target itself. func ReplaceProgram(target *ebpf.Program) Anchor { return anchor{target, sys.BPF_F_REPLACE} } // Before is the position just in front of target. func BeforeProgramByID(target ebpf.ProgramID) Anchor { return anchor{target, sys.BPF_F_BEFORE} } // After is the position just after target. func AfterProgramByID(target ebpf.ProgramID) Anchor { return anchor{target, sys.BPF_F_AFTER} } // Replace the target itself. func ReplaceProgramByID(target ebpf.ProgramID) Anchor { return anchor{target, sys.BPF_F_REPLACE} } type anchor struct { target any position uint32 } func (ap anchor) anchor() (fdOrID, flags uint32, _ error) { var typeFlag uint32 switch target := ap.target.(type) { case *ebpf.Program: fd := target.FD() if fd < 0 { return 0, 0, sys.ErrClosedFd } fdOrID = uint32(fd) typeFlag = 0 case ebpf.ProgramID: fdOrID = uint32(target) typeFlag = sys.BPF_F_ID case interface{ FD() int }: fd := target.FD() if fd < 0 { return 0, 0, sys.ErrClosedFd } fdOrID = uint32(fd) typeFlag = sys.BPF_F_LINK_MPROG case ID: fdOrID = uint32(target) typeFlag = sys.BPF_F_LINK_MPROG | sys.BPF_F_ID default: return 0, 0, fmt.Errorf("invalid target %T", ap.target) } return fdOrID, ap.position | typeFlag, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/cgroup.go000066400000000000000000000123741520243672000217710ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "fmt" "os" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type cgroupAttachFlags uint32 const ( // Allow programs attached to sub-cgroups to override the verdict of this // program. flagAllowOverride cgroupAttachFlags = 1 << iota // Allow attaching multiple programs to the cgroup. Only works if the cgroup // has zero or more programs attached using the Multi flag. Implies override. flagAllowMulti // Set automatically by progAttachCgroup.Update(). Used for updating a // specific given program attached in multi-mode. flagReplace ) type CgroupOptions struct { // Path to a cgroupv2 folder. Path string // One of the AttachCgroup* constants Attach ebpf.AttachType // Program must be of type CGroup*, and the attach type must match Attach. Program *ebpf.Program } // AttachCgroup links a BPF program to a cgroup. // // If the running kernel doesn't support bpf_link, attempts to emulate its // semantics using the legacy PROG_ATTACH mechanism. If bpf_link is not // available, the returned [Link] will not support pinning to bpffs. // // If you need more control over attachment flags or the attachment mechanism // used, look at [RawAttachProgram] and [AttachRawLink] instead. func AttachCgroup(opts CgroupOptions) (cg Link, err error) { cgroup, err := os.Open(opts.Path) if err != nil { return nil, fmt.Errorf("can't open cgroup: %s", err) } defer func() { if _, ok := cg.(*progAttachCgroup); ok { // Skip closing the cgroup handle if we return a valid progAttachCgroup, // where the handle is retained to implement Update(). return } cgroup.Close() }() cg, err = newLinkCgroup(cgroup, opts.Attach, opts.Program) if err == nil { return cg, nil } if errors.Is(err, ErrNotSupported) { cg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowMulti) } if errors.Is(err, ErrNotSupported) { cg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowOverride) } if err != nil { return nil, err } return cg, nil } type progAttachCgroup struct { cgroup *os.File current *ebpf.Program attachType ebpf.AttachType flags cgroupAttachFlags } var _ Link = (*progAttachCgroup)(nil) func (cg *progAttachCgroup) isLink() {} // newProgAttachCgroup attaches prog to cgroup using BPF_PROG_ATTACH. // cgroup and prog are retained by [progAttachCgroup]. func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) { if flags&flagAllowMulti > 0 { if err := haveProgAttachReplace(); err != nil { return nil, fmt.Errorf("can't support multiple programs: %w", err) } } // Use a program handle that cannot be closed by the caller. clone, err := prog.Clone() if err != nil { return nil, err } err = RawAttachProgram(RawAttachProgramOptions{ Target: int(cgroup.Fd()), Program: clone, Flags: uint32(flags), Attach: attach, }) if err != nil { clone.Close() return nil, fmt.Errorf("cgroup: %w", err) } return &progAttachCgroup{cgroup, clone, attach, flags}, nil } func (cg *progAttachCgroup) Close() error { defer cg.cgroup.Close() defer cg.current.Close() err := RawDetachProgram(RawDetachProgramOptions{ Target: int(cg.cgroup.Fd()), Program: cg.current, Attach: cg.attachType, }) if err != nil { return fmt.Errorf("close cgroup: %s", err) } return nil } func (cg *progAttachCgroup) Update(prog *ebpf.Program) error { new, err := prog.Clone() if err != nil { return err } args := RawAttachProgramOptions{ Target: int(cg.cgroup.Fd()), Program: prog, Attach: cg.attachType, Flags: uint32(cg.flags), } if cg.flags&flagAllowMulti > 0 { // Atomically replacing multiple programs requires at least // 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf // program in MULTI mode") args.Anchor = ReplaceProgram(cg.current) } if err := RawAttachProgram(args); err != nil { new.Close() return fmt.Errorf("can't update cgroup: %s", err) } cg.current.Close() cg.current = new return nil } func (cg *progAttachCgroup) Pin(string) error { return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported) } func (cg *progAttachCgroup) Unpin() error { return fmt.Errorf("can't unpin cgroup: %w", ErrNotSupported) } func (cg *progAttachCgroup) Detach() error { return fmt.Errorf("can't detach cgroup: %w", ErrNotSupported) } func (cg *progAttachCgroup) Info() (*Info, error) { return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported) } type linkCgroup struct { RawLink } var _ Link = (*linkCgroup)(nil) // newLinkCgroup attaches prog to cgroup using BPF_LINK_CREATE. func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) { link, err := AttachRawLink(RawLinkOptions{ Target: int(cgroup.Fd()), Program: prog, Attach: attach, }) if err != nil { return nil, err } return &linkCgroup{*link}, err } func (cg *linkCgroup) Info() (*Info, error) { var info sys.CgroupLinkInfo if err := sys.ObjInfo(cg.fd, &info); err != nil { return nil, fmt.Errorf("cgroup link info: %s", err) } extra := &CgroupInfo{ CgroupId: info.CgroupId, AttachType: info.AttachType, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/cgroup_test.go000066400000000000000000000032271520243672000230250ustar00rootroot00000000000000//go:build !windows package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestAttachCgroup(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := AttachCgroup(CgroupOptions{ Path: cgroup.Name(), Attach: ebpf.AttachCGroupInetEgress, Program: prog, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer link.Close() if haveBPFLink() == nil { if _, ok := link.(*linkCgroup); !ok { t.Fatalf("Have support for bpf_link, but got %T instead of linkCgroup", link) } } else { if _, ok := link.(*progAttachCgroup); !ok { t.Fatalf("Expected progAttachCgroup, got %T instead", link) } } } func TestProgAttachCgroup(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, 0) if err != nil { t.Fatal("Can't create link:", err) } testLink(t, link, prog) } func TestProgAttachCgroupAllowMulti(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, flagAllowMulti) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create link:", err) } // It's currently not possible for a program to replace // itself. prog2 := mustLoadProgram(t, ebpf.CGroupSKB, ebpf.AttachCGroupInetEgress, "") testLink(t, link, prog2) } func TestLinkCgroup(t *testing.T) { cgroup, prog := mustCgroupFixtures(t) link, err := newLinkCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create link:", err) } testLink(t, link, prog) } golang-github-cilium-ebpf-0.21.0+ds1/link/doc.go000066400000000000000000000001251520243672000212260ustar00rootroot00000000000000// Package link allows attaching eBPF programs to various kernel hooks. package link golang-github-cilium-ebpf-0.21.0+ds1/link/helpers_windows_test.go000066400000000000000000000015271520243672000247430ustar00rootroot00000000000000package link import ( "errors" "os" "testing" "golang.org/x/sys/windows" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" ) // windowsProgramTypeForGUID resolves a GUID to a ProgramType. func windowsProgramTypeForGUID(tb testing.TB, guid windows.GUID) ebpf.ProgramType { programType, err := ebpf.WindowsProgramTypeForGUID(guid.String()) if errors.Is(err, os.ErrNotExist) { tb.Skipf("Attach type not found for GUID %v", guid) } qt.Assert(tb, qt.IsNil(err)) return programType } // windowsAttachTypeForGUID resolves a GUID to an AttachType. func windowsAttachTypeForGUID(tb testing.TB, guid windows.GUID) ebpf.AttachType { attachType, err := ebpf.WindowsAttachTypeForGUID(guid.String()) if errors.Is(err, os.ErrNotExist) { tb.Skipf("Attach type not found for GUID %v", guid) } qt.Assert(tb, qt.IsNil(err)) return attachType } golang-github-cilium-ebpf-0.21.0+ds1/link/iter.go000066400000000000000000000036601520243672000214330ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "io" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type IterOptions struct { // Program must be of type Tracing with attach type // AttachTraceIter. The kind of iterator to attach to is // determined at load time via the AttachTo field. // // AttachTo requires the kernel to include BTF of itself, // and it to be compiled with a recent pahole (>= 1.16). Program *ebpf.Program // Map specifies the target map for bpf_map_elem and sockmap iterators. // It may be nil. Map *ebpf.Map } // AttachIter attaches a BPF seq_file iterator. func AttachIter(opts IterOptions) (*Iter, error) { progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } var info bpfIterLinkInfoMap if opts.Map != nil { mapFd := opts.Map.FD() if mapFd < 0 { return nil, fmt.Errorf("invalid map: %w", sys.ErrClosedFd) } info.map_fd = uint32(mapFd) } attr := sys.LinkCreateIterAttr{ ProgFd: uint32(progFd), AttachType: sys.AttachType(ebpf.AttachTraceIter), IterInfo: sys.UnsafePointer(unsafe.Pointer(&info)), IterInfoLen: uint32(unsafe.Sizeof(info)), } fd, err := sys.LinkCreateIter(&attr) if err != nil { if haveFeatErr := haveBPFLink(); haveFeatErr != nil { return nil, haveFeatErr } return nil, fmt.Errorf("can't link iterator: %w", err) } return &Iter{RawLink{fd, ""}}, err } // Iter represents an attached bpf_iter. type Iter struct { RawLink } // Open creates a new instance of the iterator. // // Reading from the returned reader triggers the BPF program. func (it *Iter) Open() (io.ReadCloser, error) { attr := &sys.IterCreateAttr{ LinkFd: it.fd.Uint(), } fd, err := sys.IterCreate(attr) if err != nil { return nil, fmt.Errorf("can't create iterator: %w", err) } return fd.File("bpf_iter") } // union bpf_iter_link_info.map type bpfIterLinkInfoMap struct { map_fd uint32 } golang-github-cilium-ebpf-0.21.0+ds1/link/iter_test.go000066400000000000000000000041461520243672000224720ustar00rootroot00000000000000//go:build !windows package link import ( "io" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestIter(t *testing.T) { testutils.SkipOnOldKernel(t, "5.9", "bpf_map iter") prog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, "bpf_map") it, err := AttachIter(IterOptions{ Program: prog, }) if err != nil { t.Fatal("Can't create iter:", err) } file, err := it.Open() if err != nil { t.Fatal("Can't open iter instance:", err) } defer file.Close() contents, err := io.ReadAll(file) if err != nil { t.Fatal(err) } if len(contents) != 0 { t.Error("Non-empty output from no-op iterator:", string(contents)) } testLink(t, it, prog) } func TestIterMapElements(t *testing.T) { testutils.SkipOnOldKernel(t, "5.9", "bpf_map_elem iter") prog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, "bpf_map_elem") arr, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.Array, KeySize: 4, ValueSize: 4, MaxEntries: 3, }) if err != nil { t.Fatal(err) } defer arr.Close() it, err := AttachIter(IterOptions{ Program: prog, Map: arr, }) if err != nil { t.Fatal("Can't create iter:", err) } defer it.Close() file, err := it.Open() if err != nil { t.Fatal("Can't open iter instance:", err) } defer file.Close() contents, err := io.ReadAll(file) if err != nil { t.Fatal(err) } if len(contents) != 0 { t.Error("Non-empty output from no-op iterator:", string(contents)) } } func TestUDPIter(t *testing.T) { // Introduced by 5788b3a07fc5 ("net: bpf: Implement bpf iterator for udp") testutils.SkipOnOldKernel(t, "5.9", "udp iter") prog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, "udp") it, err := AttachIter(IterOptions{ Program: prog, }) if err != nil { t.Fatal("Can't create iter:", err) } file, err := it.Open() if err != nil { t.Fatal("Can't open iter instance:", err) } defer file.Close() contents, err := io.ReadAll(file) if err != nil { t.Fatal(err) } if len(contents) != 0 { t.Error("Non-empty output from no-op iterator:", string(contents)) } testLink(t, it, prog) } golang-github-cilium-ebpf-0.21.0+ds1/link/kprobe.go000066400000000000000000000300541520243672000217470ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "fmt" "os" "runtime" "strings" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) // KprobeOptions defines additional parameters that will be used // when loading Kprobes. type KprobeOptions struct { // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. // // Needs kernel 5.15+. Cookie uint64 // Offset of the kprobe relative to the traced symbol. // Can be used to insert kprobes at arbitrary offsets in kernel functions, // e.g. in places where functions have been inlined. Offset uint64 // Increase the maximum number of concurrent invocations of a kretprobe. // Required when tracing some long running functions in the kernel. // // Warning: this setting forces the use of an outdated kernel API and is // not portable across kernel versions. On supported kernels, consider using // fexit programs instead, as they don't have this MaxActive limitation. RetprobeMaxActive int // Prefix used for the event name if the kprobe must be attached using tracefs. // The group name will be formatted as `_`. // The default empty string is equivalent to "ebpf" as the prefix. TraceFSPrefix string } func (ko *KprobeOptions) cookie() uint64 { if ko == nil { return 0 } return ko.Cookie } // Kprobe attaches the given eBPF program to a perf event that fires when the // given kernel symbol starts executing. See /proc/kallsyms for available // symbols. For example, printk(): // // kp, err := Kprobe("printk", prog, nil) // // Losing the reference to the resulting Link (kp) will close the Kprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // If attaching to symbol fails, automatically retries with the running // platform's syscall prefix (e.g. __x64_) to support attaching to syscalls // in a portable fashion. // // On kernels 6.11 and later, setting a kprobe on a nonexistent symbol using // tracefs incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist]. // // The returned Link may implement [PerfEvent]. func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) { k, err := kprobe(symbol, prog, opts, false) if err != nil { return nil, err } lnk, err := attachPerfEvent(k, prog, opts.cookie()) if err != nil { k.Close() return nil, err } return lnk, nil } // Kretprobe attaches the given eBPF program to a perf event that fires right // before the given kernel symbol exits, with the function stack left intact. // See /proc/kallsyms for available symbols. For example, printk(): // // kp, err := Kretprobe("printk", prog, nil) // // Losing the reference to the resulting Link (kp) will close the Kretprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // If attaching to symbol fails, automatically retries with the running // platform's syscall prefix (e.g. __x64_) to support attaching to syscalls // in a portable fashion. // // On kernels 5.10 and earlier, setting a kretprobe on a nonexistent symbol // incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist]. // // The returned Link may implement [PerfEvent]. func Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) { k, err := kprobe(symbol, prog, opts, true) if err != nil { return nil, err } lnk, err := attachPerfEvent(k, prog, opts.cookie()) if err != nil { k.Close() return nil, err } return lnk, nil } // isValidKprobeSymbol implements the equivalent of a regex match // against "^[a-zA-Z_][0-9a-zA-Z_.]*$". func isValidKprobeSymbol(s string) bool { if len(s) < 1 { return false } for i, c := range []byte(s) { switch { case c >= 'a' && c <= 'z': case c >= 'A' && c <= 'Z': case c == '_': case i > 0 && c >= '0' && c <= '9': // Allow `.` in symbol name. GCC-compiled kernel may change symbol name // to have a `.isra.$n` suffix, like `udp_send_skb.isra.52`. // See: https://gcc.gnu.org/gcc-10/changes.html case i > 0 && c == '.': default: return false } } return true } // kprobe opens a perf event on the given symbol and attaches prog to it. // If ret is true, create a kretprobe. func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*perfEvent, error) { if symbol == "" { return nil, fmt.Errorf("symbol name cannot be empty: %w", errInvalidInput) } if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if !isValidKprobeSymbol(symbol) { return nil, fmt.Errorf("symbol '%s' must be a valid symbol in /proc/kallsyms: %w", symbol, errInvalidInput) } if prog.Type() != ebpf.Kprobe { return nil, fmt.Errorf("eBPF program type %s is not a Kprobe: %w", prog.Type(), errInvalidInput) } args := tracefs.ProbeArgs{ Type: tracefs.Kprobe, Pid: perfAllThreads, Symbol: symbol, Ret: ret, } if opts != nil { args.RetprobeMaxActive = opts.RetprobeMaxActive args.Cookie = opts.Cookie args.Offset = opts.Offset args.Group = opts.TraceFSPrefix } // Use kprobe PMU if the kernel has it available. tp, err := pmuProbe(args) if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { if prefix := linux.PlatformPrefix(); prefix != "" { args.Symbol = prefix + symbol tp, err = pmuProbe(args) } } if err == nil { return tp, nil } if !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("creating perf_kprobe PMU (arch-specific fallback for %q): %w", symbol, err) } // Use tracefs if kprobe PMU is missing. args.Symbol = symbol tp, err = tracefsProbe(args) if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { if prefix := linux.PlatformPrefix(); prefix != "" { args.Symbol = prefix + symbol tp, err = tracefsProbe(args) } } if err != nil { return nil, fmt.Errorf("creating tracefs event (arch-specific fallback for %q): %w", symbol, err) } return tp, nil } // pmuProbe opens a perf event based on a Performance Monitoring Unit. // // Requires at least a 4.17 kernel. // e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU" // 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU" // // Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU func pmuProbe(args tracefs.ProbeArgs) (*perfEvent, error) { // Getting the PMU type will fail if the kernel doesn't support // the perf_[k,u]probe PMU. eventType, err := internal.ReadUint64FromFileOnce("%d\n", "/sys/bus/event_source/devices", args.Type.String(), "type") if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("%s: %w", args.Type, ErrNotSupported) } if err != nil { return nil, err } // Use tracefs if we want to set kretprobe's retprobeMaxActive. if args.RetprobeMaxActive != 0 { return nil, fmt.Errorf("pmu probe: non-zero retprobeMaxActive: %w", ErrNotSupported) } var config uint64 if args.Ret { bit, err := internal.ReadUint64FromFileOnce("config:%d\n", "/sys/bus/event_source/devices", args.Type.String(), "/format/retprobe") if err != nil { return nil, err } config |= 1 << bit } var ( attr unix.PerfEventAttr sp unsafe.Pointer token string ) switch args.Type { case tracefs.Kprobe: // Create a pointer to a NUL-terminated string for the kernel. sp, err = unsafeStringPtr(args.Symbol) if err != nil { return nil, err } token = tracefs.KprobeToken(args) attr = unix.PerfEventAttr{ // The minimum size required for PMU kprobes is PERF_ATTR_SIZE_VER1, // since it added the config2 (Ext2) field. Use Ext2 as probe_offset. Size: unix.PERF_ATTR_SIZE_VER1, Type: uint32(eventType), // PMU event type read from sysfs Ext1: uint64(uintptr(sp)), // Kernel symbol to trace Ext2: args.Offset, // Kernel symbol offset Config: config, // Retprobe flag } case tracefs.Uprobe: sp, err = unsafeStringPtr(args.Path) if err != nil { return nil, err } if args.RefCtrOffset != 0 { config |= args.RefCtrOffset << uprobeRefCtrOffsetShift } token = tracefs.UprobeToken(args) attr = unix.PerfEventAttr{ // The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1, // since it added the config2 (Ext2) field. The Size field controls the // size of the internal buffer the kernel allocates for reading the // perf_event_attr argument from userspace. Size: unix.PERF_ATTR_SIZE_VER1, Type: uint32(eventType), // PMU event type read from sysfs Ext1: uint64(uintptr(sp)), // Uprobe path Ext2: args.Offset, // Uprobe offset Config: config, // RefCtrOffset, Retprobe flag } } cpu := 0 if args.Pid != perfAllThreads { cpu = -1 } rawFd, err := unix.PerfEventOpen(&attr, args.Pid, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC) // On some old kernels, kprobe PMU doesn't allow `.` in symbol names and // return -EINVAL. Return ErrNotSupported to allow falling back to tracefs. // https://github.com/torvalds/linux/blob/94710cac0ef4/kernel/trace/trace_kprobe.c#L340-L343 if errors.Is(err, unix.EINVAL) && strings.Contains(args.Symbol, ".") { return nil, fmt.Errorf("token %s: older kernels don't accept dots: %w", token, ErrNotSupported) } // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // when trying to create a retprobe for a missing symbol. if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("token %s: not found: %w", token, err) } // Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved // to an invalid insn boundary. The exact conditions that trigger this error are // arch specific however. if errors.Is(err, unix.EILSEQ) { return nil, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist) } // Since at least commit cb9a19fe4aa51, ENOTSUPP is returned // when attempting to set a uprobe on a trap instruction. if errors.Is(err, sys.ENOTSUPP) { return nil, fmt.Errorf("token %s: failed setting uprobe on offset %#x (possible trap insn): %w", token, args.Offset, err) } if err != nil { return nil, fmt.Errorf("token %s: opening perf event: %w", token, err) } // Ensure the string pointer is not collected before PerfEventOpen returns. runtime.KeepAlive(sp) fd, err := sys.NewFD(rawFd) if err != nil { return nil, err } // Kernel has perf_[k,u]probe PMU available, initialize perf event. return newPerfEvent(fd, nil), nil } // tracefsProbe creates a trace event by writing an entry to /[k,u]probe_events. // A new trace event group name is generated on every call to support creating // multiple trace events for the same kernel or userspace symbol. // Path and offset are only set in the case of uprobe(s) and are used to set // the executable/library path on the filesystem and the offset where the probe is inserted. // A perf event is then opened on the newly-created trace event and returned to the caller. func tracefsProbe(args tracefs.ProbeArgs) (*perfEvent, error) { groupPrefix := "ebpf" if args.Group != "" { groupPrefix = args.Group } // Generate a random string for each trace event we attempt to create. // This value is used as the 'group' token in tracefs to allow creating // multiple kprobe trace events with the same name. group, err := tracefs.RandomGroup(groupPrefix) if err != nil { return nil, fmt.Errorf("randomizing group name: %w", err) } args.Group = group // Create the [k,u]probe trace event using tracefs. evt, err := tracefs.NewEvent(args) if err != nil { return nil, fmt.Errorf("creating probe entry on tracefs: %w", err) } // Kprobes are ephemeral tracepoints and share the same perf event type. fd, err := openTracepointPerfEvent(evt.ID(), args.Pid) if err != nil { // Make sure we clean up the created tracefs event when we return error. // If a livepatch handler is already active on the symbol, the write to // tracefs will succeed, a trace event will show up, but creating the // perf event will fail with EBUSY. _ = evt.Close() return nil, err } return newPerfEvent(fd, evt), nil } golang-github-cilium-ebpf-0.21.0+ds1/link/kprobe_multi.go000066400000000000000000000124631520243672000231650ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "fmt" "os" "github.com/cilium/ebpf" "github.com/cilium/ebpf/features" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // KprobeMultiOptions defines additional parameters that will be used // when opening a KprobeMulti Link. type KprobeMultiOptions struct { // Symbols takes a list of kernel symbol names to attach an ebpf program to. // // Mutually exclusive with Addresses. Symbols []string // Addresses takes a list of kernel symbol addresses in case they can not // be referred to by name. // // Note that only start addresses can be specified, since the fprobe API // limits the attach point to the function entry or return. // // Mutually exclusive with Symbols. Addresses []uintptr // Cookies specifies arbitrary values that can be fetched from an eBPF // program via `bpf_get_attach_cookie()`. // // If set, its length should be equal to the length of Symbols or Addresses. // Each Cookie is assigned to the Symbol or Address specified at the // corresponding slice index. Cookies []uint64 // Session must be true when attaching Programs with the // [ebpf.AttachTraceKprobeSession] attach type. // // This makes a Kprobe execute on both function entry and return. The entry // program can share a cookie value with the return program and can decide // whether the return program gets executed. Session bool } // KprobeMulti attaches the given eBPF program to the entry point of a given set // of kernel symbols. // // The difference with Kprobe() is that multi-kprobe accomplishes this in a // single system call, making it significantly faster than attaching many // probes one at a time. // // Requires at least Linux 5.18. func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) { return kprobeMulti(prog, opts, 0) } // KretprobeMulti attaches the given eBPF program to the return point of a given // set of kernel symbols. // // The difference with Kretprobe() is that multi-kprobe accomplishes this in a // single system call, making it significantly faster than attaching many // probes one at a time. // // Requires at least Linux 5.18. func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) { return kprobeMulti(prog, opts, sys.BPF_F_KPROBE_MULTI_RETURN) } func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) { if prog == nil { return nil, errors.New("cannot attach a nil program") } syms := uint32(len(opts.Symbols)) addrs := uint32(len(opts.Addresses)) cookies := uint32(len(opts.Cookies)) if syms == 0 && addrs == 0 { return nil, fmt.Errorf("one of Symbols or Addresses is required: %w", errInvalidInput) } if syms != 0 && addrs != 0 { return nil, fmt.Errorf("fields Symbols and Addresses are mutually exclusive: %w", errInvalidInput) } if cookies > 0 && cookies != syms && cookies != addrs { return nil, fmt.Errorf("field Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput) } attachType := sys.BPF_TRACE_KPROBE_MULTI if opts.Session { attachType = sys.BPF_TRACE_KPROBE_SESSION } attr := &sys.LinkCreateKprobeMultiAttr{ ProgFd: uint32(prog.FD()), AttachType: attachType, KprobeMultiFlags: flags, } switch { case syms != 0: attr.Count = syms attr.Syms = sys.NewStringSlicePointer(opts.Symbols) case addrs != 0: attr.Count = addrs attr.Addrs = sys.SlicePointer(opts.Addresses) } if cookies != 0 { attr.Cookies = sys.SlicePointer(opts.Cookies) } fd, err := sys.LinkCreateKprobeMulti(attr) if err == nil { return &kprobeMultiLink{RawLink{fd, ""}}, nil } if errors.Is(err, unix.ESRCH) { return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist) } if opts.Session { if haveFeatErr := features.HaveBPFLinkKprobeSession(); haveFeatErr != nil { return nil, haveFeatErr } } else { if haveFeatErr := features.HaveBPFLinkKprobeMulti(); haveFeatErr != nil { return nil, haveFeatErr } } // Check EINVAL after running feature probes, since it's also returned when // the kernel doesn't support the multi/session attach types. if errors.Is(err, unix.EINVAL) { return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not %s?)", err, ebpf.AttachType(attachType)) } return nil, err } type kprobeMultiLink struct { RawLink } var _ Link = (*kprobeMultiLink)(nil) func (kml *kprobeMultiLink) Update(_ *ebpf.Program) error { return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported) } func (kml *kprobeMultiLink) Info() (*Info, error) { var info sys.KprobeMultiLinkInfo if err := sys.ObjInfo(kml.fd, &info); err != nil { return nil, fmt.Errorf("kprobe multi link info: %w", err) } var addrs = make([]uint64, info.Count) var cookies = make([]uint64, info.Count) info = sys.KprobeMultiLinkInfo{ Addrs: sys.SlicePointer(addrs), Cookies: sys.SlicePointer(cookies), Count: uint32(len(addrs)), } if err := sys.ObjInfo(kml.fd, &info); err != nil { return nil, fmt.Errorf("kprobe multi link info: %w", err) } if info.Addrs.IsNil() { addrs = nil } if info.Cookies.IsNil() { cookies = nil } extra := &KprobeMultiInfo{ Count: info.Count, Flags: info.Flags, Missed: info.Missed, addrs: addrs, cookies: cookies, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/kprobe_multi_test.go000066400000000000000000000114111520243672000242140ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "os" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/features" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) var kprobeMultiSyms = []string{"vprintk", "inet6_release"} func TestKprobeMulti(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "") km, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: kprobeMultiSyms}) if err != nil { t.Fatal(err) } defer km.Close() testLink(t, km, prog) } func TestKprobeMultiInfo(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti()) testutils.SkipOnOldKernel(t, "6.6", "bpf_link_info_kprobe_multi") prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "") km, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: kprobeMultiSyms}) if err != nil { t.Fatal(err) } defer km.Close() info, err := km.Info() if err != nil { t.Fatal(err) } kmInfo := info.KprobeMulti() addresses, ok := kmInfo.Addresses() qt.Assert(t, qt.IsTrue(ok)) // kprobe_multi only returns addresses, no symbols, so we can't verify that the addresses are correct qt.Assert(t, qt.HasLen(addresses, len(kprobeMultiSyms))) } func TestKprobeMultiInput(t *testing.T) { // Program type that loads on all kernels. Not expected to link successfully. prog := mustLoadProgram(t, ebpf.SocketFilter, 0, "") // One of Symbols or Addresses must be given. _, err := KprobeMulti(prog, KprobeMultiOptions{}) if !errors.Is(err, errInvalidInput) { t.Fatalf("expected errInvalidInput, got: %v", err) } // Symbols and Addresses are mutually exclusive. _, err = KprobeMulti(prog, KprobeMultiOptions{ Symbols: []string{"foo"}, Addresses: []uintptr{1}, }) if !errors.Is(err, errInvalidInput) { t.Fatalf("expected errInvalidInput, got: %v", err) } // One Symbol, two cookies.. _, err = KprobeMulti(prog, KprobeMultiOptions{ Symbols: []string{"one"}, Cookies: []uint64{2, 3}, }) if !errors.Is(err, errInvalidInput) { t.Fatalf("expected errInvalidInput, got: %v", err) } } func TestKprobeMultiErrors(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "") // Nonexistent kernel symbol. _, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: []string{"bogus"}}) if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.EINVAL) { t.Fatalf("expected ErrNotExist or EINVAL, got: %s", err) } // Only have a negative test for addresses as it would be hard to maintain a // proper one. _, err = KprobeMulti(prog, KprobeMultiOptions{ Addresses: []uintptr{^uintptr(0)}, }) if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.EINVAL) { t.Fatalf("expected ErrNotExist or EINVAL, got: %s", err) } } func TestKprobeMultiCookie(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, "") km, err := KprobeMulti(prog, KprobeMultiOptions{ Symbols: kprobeMultiSyms, Cookies: []uint64{0, 1}, }) if err != nil { t.Fatal(err) } _ = km.Close() } func TestKprobeMultiProgramCall(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti()) m, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti) // Use actual syscall names with platform prefix. // For simplicity, just assert the increment happens with any symbol in the array. prefix := linux.PlatformPrefix() opts := KprobeMultiOptions{ Symbols: []string{prefix + "sys_getpid", prefix + "sys_gettid"}, } km, err := KprobeMulti(p, opts) if err != nil { t.Fatal(err) } // Trigger ebpf program call. unix.Getpid() unix.Gettid() // Assert that the value got incremented to at least 2, while allowing // for bigger values, because we could race with other getpid/gettid // callers. assertMapValueGE(t, m, 0, 2) // Close the link. if err := km.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. unix.Getpid() unix.Gettid() // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) } func TestKprobeSession(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeSession()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeSession, "") km, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: kprobeMultiSyms, Session: true}) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) defer km.Close() testLink(t, km, prog) } golang-github-cilium-ebpf-0.21.0+ds1/link/kprobe_test.go000066400000000000000000000261551520243672000230150ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "os" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) // Global symbol, present on all tested kernels. var ksym = "vprintk" // Collection of various symbols present in all tested kernels. // Compiler optimizations result in different names for these symbols. var symTests = []string{ "echo_char.isra.0", // function optimized by -fipa-sra "proc_get_long.constprop.0", // optimized function with constant operands "unregister_kprobes.part.0", // function body that was split and partially inlined } func TestKprobe(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") for _, tt := range symTests { t.Run(tt, func(t *testing.T) { k, err := Kprobe(tt, prog, nil) if err != nil { t.Fatal(err) } defer k.Close() }) } k, err := Kprobe("bogus", prog, nil) qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist), qt.Commentf("got error: %s", err)) if k != nil { k.Close() } k, err = Kprobe(ksym, prog, nil) qt.Assert(t, qt.IsNil(err)) defer k.Close() testLink(t, k, prog) } func TestKprobeInfo(t *testing.T) { testutils.SkipOnOldKernel(t, "6.6", "bpf_link_info_perf_event") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") k, err := Kprobe(ksym, prog, nil) qt.Assert(t, qt.IsNil(err)) defer k.Close() info, err := k.Info() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(info.PerfEvent().Kprobe().Function, ksym)) qt.Assert(t, qt.Equals(info.PerfEvent().Kprobe().Offset, 0)) } func TestKprobeOffset(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // The layout of a function is compiler and arch dependent, so we try to // find a valid attach target in the first few bytes of the function. for i := uint64(1); i < 16; i++ { k, err := Kprobe("inet6_release", prog, &KprobeOptions{Offset: i}) if err != nil { continue } k.Close() return } t.Fatal("Can't attach with non-zero offset") } func TestKretprobeMaxActive(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") defer prog.Close() _, err := Kprobe("do_sys_open", prog, &KprobeOptions{RetprobeMaxActive: 4096}) if !errors.Is(err, tracefs.ErrInvalidMaxActive) { t.Fatal("Expected ErrInvalidMaxActive, got", err) } k, err := Kretprobe("__put_task_struct", prog, &KprobeOptions{RetprobeMaxActive: 4096}) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Kretprobe with maxactive returned an error:", err) } if err := k.Close(); err != nil { t.Fatal("Closing kretprobe:", err) } } func TestKretprobe(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") for _, tt := range symTests { t.Run(tt, func(t *testing.T) { k, err := Kretprobe(tt, prog, nil) if err != nil { t.Fatal(err) } defer k.Close() }) } k, err := Kretprobe("bogus", prog, nil) if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.EINVAL) { t.Fatal(err) } if k != nil { k.Close() } k, err = Kretprobe(ksym, prog, nil) qt.Assert(t, qt.IsNil(err)) defer k.Close() testLink(t, k, prog) } func TestKprobeErrors(t *testing.T) { // Invalid Kprobe incantations. Kretprobe uses the same code paths // with a different ret flag. _, err := Kprobe("", nil, nil) // empty symbol qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = Kprobe("_", nil, nil) // empty prog qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = Kprobe(".", &ebpf.Program{}, nil) // illegal chars in symbol qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = Kprobe("foo", &ebpf.Program{}, nil) // wrong prog type qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) } // Test k(ret)probe creation using perf_kprobe PMU. func TestKprobeCreatePMU(t *testing.T) { // Requires at least 4.17 (e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU") testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU") // kprobe happy path. printk is always present. pk, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) qt.Assert(t, qt.IsNil(err)) defer pk.Close() // kretprobe happy path. pr, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Ret: true}) qt.Assert(t, qt.IsNil(err)) defer pr.Close() // Expect os.ErrNotExist when specifying a non-existent kernel symbol // on kernels 4.17 and up. _, err = pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: "bogus"}) qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist), qt.Commentf("got error: %s", err)) // A kernel bug was fixed in 97c753e62e6c where EINVAL was returned instead // of ENOENT, but only for kretprobes. _, err = pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: "bogus", Ret: true}) qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist), qt.Commentf("got error: %s", err)) } // Test fallback behaviour on kernels without perf_kprobe PMU available. func TestKprobePMUUnavailable(t *testing.T) { pk, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) if err == nil { pk.Close() t.Skipf("Kernel supports perf_kprobe PMU, not asserting error.") } // Only allow a PMU creation with a valid kernel symbol to fail with ErrNotSupported. qt.Assert(t, qt.ErrorIs(err, ErrNotSupported), qt.Commentf("got error: %s", err)) } func BenchmarkKprobeCreatePMU(b *testing.B) { for b.Loop() { pr, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) if err != nil { b.Error("error creating perf_kprobe PMU:", err) } if err := pr.Close(); err != nil { b.Error("error closing perf_kprobe PMU:", err) } } } // Test tracefs k(ret)probe creation on all kernel versions. func TestKprobeTraceFS(t *testing.T) { // Open and close tracefs k(ret)probes, checking all errors. kp, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(kp.Close())) kp, err = tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Ret: true}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(kp.Close())) // Create two identical trace events, ensure their IDs differ. k1, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) qt.Assert(t, qt.IsNil(err)) defer k1.Close() qt.Assert(t, qt.IsNotNil(k1.tracefsEvent)) k2, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym}) qt.Assert(t, qt.IsNil(err)) defer k2.Close() qt.Assert(t, qt.IsNotNil(k2.tracefsEvent)) // Compare the kprobes' tracefs IDs. qt.Assert(t, qt.Not(qt.Equals(k1.tracefsEvent.ID(), k2.tracefsEvent.ID()))) // Expect an error when supplying an invalid custom group name _, err = tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Group: "/"}) qt.Assert(t, qt.Not(qt.IsNil(err))) cg := "customgroup" k3, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Group: cg}) qt.Assert(t, qt.IsNil(err)) defer k3.Close() qt.Assert(t, qt.Matches(k3.tracefsEvent.Group(), `customgroup_[a-f0-9]{16}`)) // Prepare probe args. args := tracefs.ProbeArgs{Type: tracefs.Kprobe, Group: "testgroup", Symbol: "symbol"} // Write a k(ret)probe event for a non-existing symbol. _, err = tracefs.NewEvent(args) // A kernel bug was introduced in 9d8616034f16 that causes EINVAL to be returned // instead of ENOENT when trying to attach kprobes to non-existing symbols. qt.Assert(t, qt.IsTrue(errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL)), qt.Commentf("got error: %s", err)) // A kernel bug was fixed in 97c753e62e6c where EINVAL was returned instead // of ENOENT, but only for kretprobes. args.Ret = true _, err = tracefs.NewEvent(args) qt.Assert(t, qt.IsTrue(errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL)), qt.Commentf("got error: %s", err)) } func BenchmarkKprobeCreateTraceFS(b *testing.B) { for b.Loop() { // Include /kprobe_events operations in the benchmark loop // because we create one per perf event. pr, err := tracefsProbe(tracefs.ProbeArgs{Symbol: ksym}) if err != nil { b.Error("error creating tracefs perf event:", err) } if err := pr.Close(); err != nil { b.Error("error closing tracefs perf event:", err) } } } func TestKprobeProgramCall(t *testing.T) { m, p := newUpdaterMapProg(t, ebpf.Kprobe, 0) // Open Kprobe on `sys_getpid` and attach it // to the ebpf program created above. k, err := Kprobe("sys_getpid", p, nil) if err != nil { t.Fatal(err) } // Trigger ebpf program call. unix.Getpid() // Assert that the value got incremented to at least 1, while allowing // for bigger values, because we could race with other getpid callers. assertMapValueGE(t, m, 0, 1) // Detach the Kprobe. if err := k.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. unix.Getpid() // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) } func newUpdaterMapProg(t *testing.T, typ ebpf.ProgramType, attach ebpf.AttachType) (*ebpf.Map, *ebpf.Program) { // Create ebpf map. Will contain only one key with initial value 0. m, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }) if err != nil { t.Fatal(err) } // Create ebpf program. When called, will increase the value of key 0 by 1 // in the map created above. p, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: typ, Instructions: asm.Instructions{ // R1 map asm.LoadMapPtr(asm.R1, m.FD()), // R2 key asm.Mov.Reg(asm.R2, asm.R10), asm.Add.Imm(asm.R2, -4), asm.StoreImm(asm.R2, 0, 0, asm.Word), // Lookup map[0] asm.FnMapLookupElem.Call(), asm.JEq.Imm(asm.R0, 0, "ret"), // u32 val = R0++ asm.LoadMem(asm.R1, asm.R0, 0, asm.Word), asm.Add.Imm(asm.R1, 1), asm.StoreMem(asm.RFP, -8, asm.R1, asm.Word), // u32 key = 0 asm.Mov.Imm(asm.R1, 0), asm.StoreMem(asm.RFP, -4, asm.R1, asm.Word), // bpf_map_update_elem(...) asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), asm.Mov.Reg(asm.R3, asm.RFP), asm.Add.Imm(asm.R3, -8), asm.LoadMapPtr(asm.R1, m.FD()), asm.Mov.Imm(asm.R4, 0), asm.FnMapUpdateElem.Call(), // exit 0 asm.Mov.Imm(asm.R0, 0), asm.Return().WithSymbol("ret"), }, AttachType: attach, License: "Dual MIT/GPL", }) if err != nil { t.Fatal(err) } // Close the program and map on test teardown. t.Cleanup(func() { m.Close() p.Close() }) return m, p } func assertMapValue(t *testing.T, m *ebpf.Map, k, v uint32) { var val uint32 if err := m.Lookup(k, &val); err != nil { t.Fatal(err) } if val != v { t.Fatalf("unexpected value: want '%d', got '%d'", v, val) } } func assertMapValueGE(t *testing.T, m *ebpf.Map, k, v uint32) { var val uint32 if err := m.Lookup(k, &val); err != nil { t.Fatal(err) } if val < v { t.Fatalf("unexpected value: want >= '%d', got '%d'", v, val) } } func TestKprobeCookie(t *testing.T) { testutils.SkipOnOldKernel(t, "5.15", "bpf_perf_link") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") k, err := Kprobe(ksym, prog, &KprobeOptions{Cookie: 1000}) if err != nil { t.Fatal(err) } k.Close() } golang-github-cilium-ebpf-0.21.0+ds1/link/link.go000066400000000000000000000166641520243672000214350ustar00rootroot00000000000000package link import ( "errors" "fmt" "os" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // Type is the kind of link. type Type = sys.LinkType var ErrNotSupported = internal.ErrNotSupported // Link represents a Program attached to a BPF hook. type Link interface { // Replace the current program with a new program. // // Passing a nil program is an error. May return an error wrapping ErrNotSupported. Update(*ebpf.Program) error // Persist a link by pinning it into a bpffs. // // May return an error wrapping ErrNotSupported. Pin(string) error // Undo a previous call to Pin. // // May return an error wrapping ErrNotSupported. Unpin() error // Close frees resources. // // The link will be broken unless it has been successfully pinned. // A link may continue past the lifetime of the process if Close is // not called. Close() error // Detach the link from its corresponding attachment point. // // May return an error wrapping ErrNotSupported. Detach() error // Info returns metadata on a link. // // May return an error wrapping ErrNotSupported. Info() (*Info, error) // Prevent external users from implementing this interface. isLink() } // NewFromFD creates a link from a raw fd. // // You should not use fd after calling this function. func NewFromFD(fd int) (Link, error) { sysFD, err := sys.NewFD(fd) if err != nil { return nil, err } return wrapRawLink(&RawLink{fd: sysFD}) } // NewFromID returns the link associated with the given id. // // Returns ErrNotExist if there is no link with the given id. func NewFromID(id ID) (Link, error) { getFdAttr := &sys.LinkGetFdByIdAttr{Id: id} fd, err := sys.LinkGetFdById(getFdAttr) if err != nil { return nil, fmt.Errorf("get link fd from ID %d: %w", id, err) } return wrapRawLink(&RawLink{fd, ""}) } // LoadPinnedLink loads a Link from a pin (file) on the BPF virtual filesystem. // // Requires at least Linux 5.7. func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { raw, err := loadPinnedRawLink(fileName, opts) if err != nil { return nil, err } return wrapRawLink(raw) } // ID uniquely identifies a BPF link. type ID = sys.LinkID // RawLinkOptions control the creation of a raw link. type RawLinkOptions struct { // File descriptor to attach to. This differs for each attach type. Target int // Program to attach. Program *ebpf.Program // Attach must match the attach type of Program. Attach ebpf.AttachType // BTF is the BTF of the attachment target. BTF btf.TypeID // Flags control the attach behaviour. Flags uint32 } // Info contains metadata on a link. type Info struct { Type Type ID ID Program ebpf.ProgramID extra interface{} } // RawLink is the low-level API to bpf_link. // // You should consider using the higher level interfaces in this // package instead. type RawLink struct { fd *sys.FD pinnedPath string } func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) { fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) if err != nil { return nil, fmt.Errorf("load pinned link: %w", err) } if typ != sys.BPF_TYPE_LINK { _ = fd.Close() return nil, fmt.Errorf("%s is not a Link", fileName) } return &RawLink{fd, fileName}, nil } func (l *RawLink) isLink() {} // FD returns the raw file descriptor. func (l *RawLink) FD() int { return l.fd.Int() } // Close breaks the link. // // Use Pin if you want to make the link persistent. func (l *RawLink) Close() error { return l.fd.Close() } // Pin persists a link past the lifetime of the process. // // Calling Close on a pinned Link will not break the link // until the pin is removed. func (l *RawLink) Pin(fileName string) error { if err := sys.Pin(l.pinnedPath, fileName, l.fd); err != nil { return err } l.pinnedPath = fileName return nil } // Unpin implements the Link interface. func (l *RawLink) Unpin() error { if err := sys.Unpin(l.pinnedPath); err != nil { return err } l.pinnedPath = "" return nil } // IsPinned returns true if the Link has a non-empty pinned path. func (l *RawLink) IsPinned() bool { return l.pinnedPath != "" } // Update implements the Link interface. func (l *RawLink) Update(new *ebpf.Program) error { return l.UpdateArgs(RawLinkUpdateOptions{ New: new, }) } // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs. type RawLinkUpdateOptions struct { New *ebpf.Program Old *ebpf.Program Flags uint32 } // UpdateArgs updates a link based on args. func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error { newFd := opts.New.FD() if newFd < 0 { return fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } var oldFd int if opts.Old != nil { oldFd = opts.Old.FD() if oldFd < 0 { return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd) } } attr := sys.LinkUpdateAttr{ LinkFd: l.fd.Uint(), NewProgFd: uint32(newFd), OldProgFd: uint32(oldFd), Flags: opts.Flags, } if err := sys.LinkUpdate(&attr); err != nil { return fmt.Errorf("update link: %w", err) } return nil } // Detach the link from its corresponding attachment point. func (l *RawLink) Detach() error { attr := sys.LinkDetachAttr{ LinkFd: l.fd.Uint(), } err := sys.LinkDetach(&attr) switch { case errors.Is(err, unix.EOPNOTSUPP): return internal.ErrNotSupported case err != nil: return fmt.Errorf("detach link: %w", err) default: return nil } } // Info returns metadata about the link. // // Linktype specific metadata is not included and can be retrieved // via the linktype specific Info() method. func (l *RawLink) Info() (*Info, error) { var info sys.LinkInfo if err := sys.ObjInfo(l.fd, &info); err != nil { return nil, fmt.Errorf("link info: %s", err) } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), nil, }, nil } // Iterator allows iterating over links attached into the kernel. type Iterator struct { // The ID of the current link. Only valid after a call to Next ID ID // The current link. Only valid until a call to Next. // See Take if you want to retain the link. Link Link err error } // Next retrieves the next link. // // Returns true if another link was found. Call [Iterator.Err] after the function returns false. func (it *Iterator) Next() bool { id := it.ID for { getIdAttr := &sys.LinkGetNextIdAttr{Id: id} err := sys.LinkGetNextId(getIdAttr) if errors.Is(err, os.ErrNotExist) { // There are no more links. break } else if err != nil { it.err = fmt.Errorf("get next link ID: %w", err) break } id = getIdAttr.NextId l, err := NewFromID(id) if errors.Is(err, os.ErrNotExist) { // Couldn't load the link fast enough. Try next ID. continue } else if err != nil { it.err = fmt.Errorf("get link for ID %d: %w", id, err) break } if it.Link != nil { it.Link.Close() } it.ID, it.Link = id, l return true } // No more links or we encountered an error. if it.Link != nil { it.Link.Close() } it.Link = nil return false } // Take the ownership of the current link. // // It's the callers responsibility to close the link. func (it *Iterator) Take() Link { l := it.Link it.Link = nil return l } // Err returns an error if iteration failed for some reason. func (it *Iterator) Err() error { return it.err } func (it *Iterator) Close() { if it.Link != nil { it.Link.Close() } } golang-github-cilium-ebpf-0.21.0+ds1/link/link_other.go000066400000000000000000000217501520243672000226260ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" ) // Valid link types. const ( UnspecifiedType = sys.BPF_LINK_TYPE_UNSPEC RawTracepointType = sys.BPF_LINK_TYPE_RAW_TRACEPOINT TracingType = sys.BPF_LINK_TYPE_TRACING CgroupType = sys.BPF_LINK_TYPE_CGROUP IterType = sys.BPF_LINK_TYPE_ITER NetNsType = sys.BPF_LINK_TYPE_NETNS XDPType = sys.BPF_LINK_TYPE_XDP PerfEventType = sys.BPF_LINK_TYPE_PERF_EVENT KprobeMultiType = sys.BPF_LINK_TYPE_KPROBE_MULTI TCXType = sys.BPF_LINK_TYPE_TCX UprobeMultiType = sys.BPF_LINK_TYPE_UPROBE_MULTI NetfilterType = sys.BPF_LINK_TYPE_NETFILTER NetkitType = sys.BPF_LINK_TYPE_NETKIT StructOpsType = sys.BPF_LINK_TYPE_STRUCT_OPS ) // AttachRawLink creates a raw link. func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { if err := haveBPFLink(); err != nil { return nil, err } if opts.Target < 0 { return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd) } progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } p, attachType := platform.DecodeConstant(opts.Attach) if p != platform.Linux { return nil, fmt.Errorf("attach type %s: %w", opts.Attach, internal.ErrNotSupportedOnOS) } attr := sys.LinkCreateAttr{ TargetFd: uint32(opts.Target), ProgFd: uint32(progFd), AttachType: sys.AttachType(attachType), TargetBtfId: opts.BTF, Flags: opts.Flags, } fd, err := sys.LinkCreate(&attr) if err != nil { return nil, fmt.Errorf("create link: %w", err) } return &RawLink{fd, ""}, nil } // wrap a RawLink in a more specific type if possible. // // The function takes ownership of raw and closes it on error. func wrapRawLink(raw *RawLink) (_ Link, err error) { defer func() { if err != nil { raw.Close() } }() info, err := raw.Info() if err != nil { return nil, err } switch info.Type { case RawTracepointType: return &rawTracepoint{*raw}, nil case TracingType: return &tracing{*raw}, nil case CgroupType: return &linkCgroup{*raw}, nil case IterType: return &Iter{*raw}, nil case NetNsType: return &NetNsLink{*raw}, nil case KprobeMultiType: return &kprobeMultiLink{*raw}, nil case UprobeMultiType: return &uprobeMultiLink{*raw}, nil case PerfEventType: return &perfEventLink{*raw, nil}, nil case TCXType: return &tcxLink{*raw}, nil case NetfilterType: return &netfilterLink{*raw}, nil case NetkitType: return &netkitLink{*raw}, nil case XDPType: return &xdpLink{*raw}, nil case StructOpsType: return &structOpsLink{*raw}, nil default: return raw, nil } } type TracingInfo struct { AttachType sys.AttachType TargetObjectId uint32 TargetBtfId sys.TypeID } type CgroupInfo struct { CgroupId uint64 AttachType sys.AttachType _ [4]byte } type NetNsInfo struct { NetnsInode uint32 AttachType sys.AttachType } type TCXInfo struct { Ifindex uint32 AttachType sys.AttachType } type XDPInfo struct { Ifindex uint32 } type NetfilterInfo struct { ProtocolFamily NetfilterProtocolFamily Hook NetfilterInetHook Priority int32 Flags uint32 } type NetkitInfo struct { Ifindex uint32 AttachType sys.AttachType } type RawTracepointInfo struct { Name string } type KprobeMultiInfo struct { // Count is the number of addresses hooked by the kprobe. Count uint32 Flags uint32 Missed uint64 addrs []uint64 cookies []uint64 } type KprobeMultiAddress struct { Address uint64 Cookie uint64 } // Addresses are the addresses hooked by the kprobe. func (kpm *KprobeMultiInfo) Addresses() ([]KprobeMultiAddress, bool) { if kpm.addrs == nil || len(kpm.addrs) != len(kpm.cookies) { return nil, false } addrs := make([]KprobeMultiAddress, len(kpm.addrs)) for i := range kpm.addrs { addrs[i] = KprobeMultiAddress{ Address: kpm.addrs[i], Cookie: kpm.cookies[i], } } return addrs, true } type UprobeMultiInfo struct { Count uint32 Flags uint32 Missed uint64 offsets []uint64 cookies []uint64 refCtrOffsets []uint64 // File is the path that the file the uprobe was attached to // had at creation time. // // However, due to various circumstances (differing mount namespaces, // file replacement, ...), this path may not point to the same binary // the uprobe was originally attached to. File string pid uint32 } type UprobeMultiOffset struct { Offset uint64 Cookie uint64 ReferenceCount uint64 } // Offsets returns the offsets that the uprobe was attached to along with the related cookies and ref counters. func (umi *UprobeMultiInfo) Offsets() ([]UprobeMultiOffset, bool) { if umi.offsets == nil || len(umi.cookies) != len(umi.offsets) || len(umi.refCtrOffsets) != len(umi.offsets) { return nil, false } var adresses = make([]UprobeMultiOffset, len(umi.offsets)) for i := range umi.offsets { adresses[i] = UprobeMultiOffset{ Offset: umi.offsets[i], Cookie: umi.cookies[i], ReferenceCount: umi.refCtrOffsets[i], } } return adresses, true } // Pid returns the process ID that this uprobe is attached to. // // If it does not exist, the uprobe will trigger for all processes. func (umi *UprobeMultiInfo) Pid() (uint32, bool) { return umi.pid, umi.pid > 0 } const ( PerfEventUnspecified = sys.BPF_PERF_EVENT_UNSPEC PerfEventUprobe = sys.BPF_PERF_EVENT_UPROBE PerfEventUretprobe = sys.BPF_PERF_EVENT_URETPROBE PerfEventKprobe = sys.BPF_PERF_EVENT_KPROBE PerfEventKretprobe = sys.BPF_PERF_EVENT_KRETPROBE PerfEventTracepoint = sys.BPF_PERF_EVENT_TRACEPOINT PerfEventEvent = sys.BPF_PERF_EVENT_EVENT ) type PerfEventInfo struct { Type sys.PerfEventType extra interface{} } func (r *PerfEventInfo) Kprobe() *KprobeInfo { e, _ := r.extra.(*KprobeInfo) return e } func (r *PerfEventInfo) Uprobe() *UprobeInfo { e, _ := r.extra.(*UprobeInfo) return e } func (r *PerfEventInfo) Tracepoint() *TracepointInfo { e, _ := r.extra.(*TracepointInfo) return e } func (r *PerfEventInfo) Event() *EventInfo { e, _ := r.extra.(*EventInfo) return e } type KprobeInfo struct { Address uint64 Missed uint64 Function string Offset uint32 } type UprobeInfo struct { // File is the path that the file the uprobe was attached to // had at creation time. // // However, due to various circumstances (differing mount namespaces, // file replacement, ...), this path may not point to the same binary // the uprobe was originally attached to. File string Offset uint32 Cookie uint64 OffsetReferenceCount uint64 } type TracepointInfo struct { Tracepoint string Cookie uint64 } type EventInfo struct { Config uint64 Type uint32 Cookie uint64 } // Tracing returns tracing type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) Tracing() *TracingInfo { e, _ := r.extra.(*TracingInfo) return e } // Cgroup returns cgroup type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) Cgroup() *CgroupInfo { e, _ := r.extra.(*CgroupInfo) return e } // NetNs returns netns type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) NetNs() *NetNsInfo { e, _ := r.extra.(*NetNsInfo) return e } // XDP returns XDP type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) XDP() *XDPInfo { e, _ := r.extra.(*XDPInfo) return e } // TCX returns TCX type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) TCX() *TCXInfo { e, _ := r.extra.(*TCXInfo) return e } // Netfilter returns netfilter type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) Netfilter() *NetfilterInfo { e, _ := r.extra.(*NetfilterInfo) return e } // Netkit returns netkit type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) Netkit() *NetkitInfo { e, _ := r.extra.(*NetkitInfo) return e } // KprobeMulti returns kprobe-multi type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) KprobeMulti() *KprobeMultiInfo { e, _ := r.extra.(*KprobeMultiInfo) return e } // UprobeMulti returns uprobe-multi type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) UprobeMulti() *UprobeMultiInfo { e, _ := r.extra.(*UprobeMultiInfo) return e } // PerfEvent returns perf-event type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) PerfEvent() *PerfEventInfo { e, _ := r.extra.(*PerfEventInfo) return e } // RawTracepoint returns raw-tracepoint type-specific link info. // // Returns nil if the type-specific link info isn't available. func (r Info) RawTracepoint() *RawTracepointInfo { e, _ := r.extra.(*RawTracepointInfo) return e } golang-github-cilium-ebpf-0.21.0+ds1/link/link_other_test.go000066400000000000000000000067251520243672000236720ustar00rootroot00000000000000//go:build !windows package link import ( "os" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" ) func testLinkArch(t *testing.T, link Link) { t.Run("link/info", func(t *testing.T) { info, err := link.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Link info returns an error:", err) } if info.Type == 0 { t.Fatal("Failed to get link info type") } switch link.(type) { case *tracing: if info.Tracing() == nil { t.Fatalf("Failed to get link tracing extra info") } case *linkCgroup: cg := info.Cgroup() if cg.CgroupId == 0 { t.Fatalf("Failed to get link Cgroup extra info") } case *NetNsLink: netns := info.NetNs() if netns.AttachType == 0 { t.Fatalf("Failed to get link NetNs extra info") } case *xdpLink: xdp := info.XDP() if xdp.Ifindex == 0 { t.Fatalf("Failed to get link XDP extra info") } case *tcxLink: tcx := info.TCX() if tcx.Ifindex == 0 { t.Fatalf("Failed to get link TCX extra info") } case *netfilterLink: nf := info.Netfilter() if nf.Priority == 0 { t.Fatalf("Failed to get link Netfilter extra info") } case *kprobeMultiLink: // test default Info data kmulti := info.KprobeMulti() // kprobe multi link info is supported since kernel 6.6 testutils.SkipOnOldKernel(t, "6.6", "bpf_kprobe_multi_link_fill_link_info") qt.Assert(t, qt.Not(qt.Equals(kmulti.Count, 0))) // NB: We don't check that missed is actually correct // since it's not easy to trigger from tests. case *perfEventLink: // test default Info data pevent := info.PerfEvent() switch pevent.Type { case sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE: _ = pevent.Kprobe() // NB: We don't check that missed is actually correct // since it's not easy to trigger from tests. // Nor do we check the address (since we don't know it here). } } }) } func newRawLink(t *testing.T) (*RawLink, *ebpf.Program) { t.Helper() cgroup, prog := mustCgroupFixtures(t) link, err := AttachRawLink(RawLinkOptions{ Target: int(cgroup.Fd()), Program: prog, Attach: ebpf.AttachCGroupInetEgress, }) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create raw link:", err) } t.Cleanup(func() { link.Close() }) return link, prog } func mustCgroupFixtures(t *testing.T) (*os.File, *ebpf.Program) { t.Helper() testutils.SkipIfNotSupported(t, haveProgAttach()) return testutils.CreateCgroup(t), mustLoadProgram(t, ebpf.CGroupSKB, 0, "") } func mustLoadProgram(tb testing.TB, typ ebpf.ProgramType, attachType ebpf.AttachType, attachTo string) *ebpf.Program { tb.Helper() license := "MIT" switch typ { case ebpf.RawTracepoint, ebpf.LSM: license = "GPL" } prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: typ, AttachType: attachType, AttachTo: attachTo, License: license, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { prog.Close() }) return prog } func TestDetachLinkFail(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") defer prog.Close() uprobeLink, err := bashEx.Uprobe(bashSym, prog, nil) qt.Assert(t, qt.IsNil(err)) defer uprobeLink.Close() err = uprobeLink.Detach() qt.Assert(t, qt.ErrorIs(err, ErrNotSupported), qt.Commentf("got error: %s", err)) } golang-github-cilium-ebpf-0.21.0+ds1/link/link_test.go000066400000000000000000000121101520243672000224520ustar00rootroot00000000000000package link import ( "errors" "math" "path/filepath" "reflect" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/testmain" "github.com/cilium/ebpf/internal/unix" ) func TestMain(m *testing.M) { testmain.Run(m) } func TestRawLink(t *testing.T) { link, prog := newRawLink(t) info, err := link.Info() if err != nil { t.Fatal("Can't get link info:", err) } pi, err := prog.Info() if err != nil { t.Fatal("Can't get program info:", err) } progID, ok := pi.ID() if !ok { t.Fatal("Program ID not available in program info") } if info.Program != progID { t.Error("Link program ID doesn't match program ID") } testLink(t, link, prog) } func TestUnpinRawLink(t *testing.T) { link, _ := newPinnedRawLink(t) qt.Assert(t, qt.IsTrue(link.IsPinned())) if err := link.Unpin(); err != nil { t.Fatal(err) } qt.Assert(t, qt.IsFalse(link.IsPinned())) } func TestDetachRawLink(t *testing.T) { link, _ := newRawLink(t) if err := link.Detach(); err != nil { t.Fatal(err) } } func TestRawLinkLoadPinnedWithOptions(t *testing.T) { link, path := newPinnedRawLink(t) defer link.Close() qt.Assert(t, qt.IsTrue(link.IsPinned())) // It seems like the kernel ignores BPF_F_RDONLY when updating a link, // so we can't test this. _, err := loadPinnedRawLink(path, &ebpf.LoadPinOptions{ Flags: math.MaxUint32, }) if !errors.Is(err, unix.EINVAL) { t.Fatal("Invalid flags don't trigger an error:", err) } } func TestIterator(t *testing.T) { tLink, _ := newPinnedRawLink(t) tLinkInfo, err := tLink.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't get original link info:", err) } it := new(Iterator) defer it.Close() prev := it.ID var foundLink Link for it.Next() { // Iterate all loaded links. if it.Link == nil { t.Fatal("Next doesn't assign link") } if it.ID == prev { t.Fatal("Iterator doesn't advance ID") } prev = it.ID if it.ID == tLinkInfo.ID { foundLink = it.Take() } } if err := it.Err(); err != nil { t.Fatal("Iteration returned an error:", err) } if it.Link != nil { t.Fatal("Next doesn't clean up link on last iteration") } if prev != it.ID { t.Fatal("Next changes ID on last iteration") } if foundLink == nil { t.Fatal("Original link not found") } defer foundLink.Close() // Confirm that we found the original link. info, err := foundLink.Info() if err != nil { t.Fatal("Can't get link info:", err) } if info.ID != tLinkInfo.ID { t.Fatal("Found link has wrong ID") } } func newPinnedRawLink(t *testing.T) (*RawLink, string) { t.Helper() link, _ := newRawLink(t) path := filepath.Join(testutils.TempBPFFS(t), "link") err := link.Pin(path) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) return link, path } func testLink(t *testing.T, link Link, prog *ebpf.Program) { t.Helper() tmp := testutils.TempBPFFS(t) _, isRawLink := link.(*RawLink) t.Run("link/pinning", func(t *testing.T) { path := filepath.Join(tmp, "link") err := link.Pin(path) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("Can't pin %T: %s", link, err) } link2, err := LoadPinnedLink(path, nil) if err != nil { t.Fatalf("Can't load pinned %T: %s", link, err) } link2.Close() if !isRawLink && reflect.TypeOf(link) != reflect.TypeOf(link2) { t.Errorf("Loading a pinned %T returns a %T", link, link2) } _, err = LoadPinnedLink(path, &ebpf.LoadPinOptions{ Flags: math.MaxUint32, }) if !errors.Is(err, unix.EINVAL) { t.Errorf("Loading a pinned %T doesn't respect flags", link) } }) t.Run("link/update", func(t *testing.T) { err := link.Update(prog) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Update returns an error:", err) } func() { // Panicking is OK defer func() { _ = recover() }() if err := link.Update(nil); err == nil { t.Fatalf("%T.Update accepts nil program", link) } }() }) testLinkArch(t, link) type FDer interface { FD() int } t.Run("from fd", func(t *testing.T) { fder, ok := link.(FDer) if !ok { t.Skip("Link doesn't allow retrieving FD") } // We need to dup the FD since NewLinkFromFD takes // ownership. dupFD := testutils.DupFD(t, fder.FD()) newLink, err := NewFromFD(dupFD) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create new link from dup link FD:", err) } defer newLink.Close() if !isRawLink && reflect.TypeOf(newLink) != reflect.TypeOf(link) { t.Fatalf("Expected type %T, got %T", link, newLink) } }) if err := link.Close(); err != nil { t.Fatalf("%T.Close returns an error: %s", link, err) } } func TestLoadWrongPin(t *testing.T) { l, p := newRawLink(t) tmp := testutils.TempBPFFS(t) ppath := filepath.Join(tmp, "prog") lpath := filepath.Join(tmp, "link") qt.Assert(t, qt.IsNil(p.Pin(ppath))) qt.Assert(t, qt.IsNil(l.Pin(lpath))) _, err := LoadPinnedLink(ppath, nil) qt.Assert(t, qt.IsNotNil(err)) ll, err := LoadPinnedLink(lpath, nil) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(ll.Close())) } golang-github-cilium-ebpf-0.21.0+ds1/link/link_windows.go000066400000000000000000000022301520243672000231670ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/efw" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" ) // AttachRawLink creates a raw link. func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { if opts.Target != 0 || opts.BTF != 0 || opts.Flags != 0 { return nil, fmt.Errorf("specified option(s) %w", internal.ErrNotSupportedOnOS) } plat, attachType := platform.DecodeConstant(opts.Attach) if plat != platform.Windows { return nil, fmt.Errorf("attach type %s: %w", opts.Attach, internal.ErrNotSupportedOnOS) } attachTypeGUID, err := efw.EbpfGetEbpfAttachType(attachType) if err != nil { return nil, fmt.Errorf("get attach type: %w", err) } progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } raw, err := efw.EbpfProgramAttachFds(progFd, attachTypeGUID, nil, 0) if err != nil { return nil, fmt.Errorf("attach link: %w", err) } fd, err := sys.NewFD(int(raw)) if err != nil { return nil, err } return &RawLink{fd: fd}, nil } func wrapRawLink(raw *RawLink) (Link, error) { return raw, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/link_windows_test.go000066400000000000000000000054171520243672000242400ustar00rootroot00000000000000package link import ( "os/exec" "testing" "github.com/go-quicktest/qt" "golang.org/x/sys/windows" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" ) // ntosebpfext has not yet assigned a stable enum value so we can't refer to // it via that (https://github.com/microsoft/ntosebpfext/issues/152). // // See https://github.com/microsoft/ntosebpfext/blob/75ceaac38a0254e44f3219852d79a336d10ad9f3/include/ebpf_ntos_program_attach_type_guids.h var ( programTypeProcessGUID = makeGUID(0x22ea7b37, 0x1043, 0x4d0d, [8]byte{0xb6, 0x0d, 0xca, 0xfa, 0x1c, 0x7b, 0x63, 0x8e}) attachTypeProcessGUID = makeGUID(0x66e20687, 0x9805, 0x4458, [8]byte{0xa0, 0xdb, 0x38, 0xe2, 0x20, 0xd3, 0x16, 0x85}) ) func testLinkArch(t *testing.T, link Link) {} func newRawLink(t *testing.T) (*RawLink, *ebpf.Program) { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.WindowsBind, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }) qt.Assert(t, qt.IsNil(err)) t.Cleanup(func() { prog.Close() }) link, err := AttachRawLink(RawLinkOptions{ Program: prog, Attach: ebpf.AttachWindowsBind, }) qt.Assert(t, qt.IsNil(err)) t.Cleanup(func() { link.Close() }) return link, prog } func TestProcessLink(t *testing.T) { array, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.WindowsArray, Name: "process_state", KeySize: 4, ValueSize: 4, MaxEntries: 1, }) qt.Assert(t, qt.IsNil(err)) defer array.Close() prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: windowsProgramTypeForGUID(t, programTypeProcessGUID), Name: "process_test", Instructions: asm.Instructions{ // R1 = map asm.LoadMapPtr(asm.R1, array.FD()), // R2 = key asm.Mov.Reg(asm.R2, asm.R10), asm.Add.Imm(asm.R2, -4), asm.StoreImm(asm.R2, 0, 0, asm.Word), // R3 = value asm.Mov.Reg(asm.R3, asm.R2), asm.Add.Imm(asm.R3, -4), asm.StoreImm(asm.R3, 0, 1, asm.Word), // R4 = flags asm.Mov.Imm(asm.R4, 0), // bpf_map_update_elem(map, key, value, flags) asm.WindowsFnMapUpdateElem.Call(), asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }) qt.Assert(t, qt.IsNil(err)) defer prog.Close() link, err := AttachRawLink(RawLinkOptions{ Program: prog, Attach: windowsAttachTypeForGUID(t, attachTypeProcessGUID), }) qt.Assert(t, qt.IsNil(err)) defer link.Close() qt.Assert(t, qt.IsNil(exec.Command("cmd.exe", "/c", "exit 0").Run())) var value uint32 qt.Assert(t, qt.IsNil(array.Lookup(uint32(0), &value))) qt.Assert(t, qt.Equals(value, 1), qt.Commentf("Executing a binary should trigger the program")) qt.Assert(t, qt.IsNil(link.Close())) } func makeGUID(data1 uint32, data2 uint16, data3 uint16, data4 [8]byte) windows.GUID { return windows.GUID{Data1: data1, Data2: data2, Data3: data3, Data4: data4} } golang-github-cilium-ebpf-0.21.0+ds1/link/netfilter.go000066400000000000000000000054671520243672000224730ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) const NetfilterIPDefrag NetfilterAttachFlags = 0 // Enable IP packet defragmentation type NetfilterAttachFlags uint32 type NetfilterInetHook = sys.NetfilterInetHook const ( NetfilterInetPreRouting = sys.NF_INET_PRE_ROUTING NetfilterInetLocalIn = sys.NF_INET_LOCAL_IN NetfilterInetForward = sys.NF_INET_FORWARD NetfilterInetLocalOut = sys.NF_INET_LOCAL_OUT NetfilterInetPostRouting = sys.NF_INET_POST_ROUTING ) type NetfilterProtocolFamily = sys.NetfilterProtocolFamily const ( NetfilterProtoUnspec = sys.NFPROTO_UNSPEC NetfilterProtoInet = sys.NFPROTO_INET // Inet applies to both IPv4 and IPv6 NetfilterProtoIPv4 = sys.NFPROTO_IPV4 NetfilterProtoARP = sys.NFPROTO_ARP NetfilterProtoNetdev = sys.NFPROTO_NETDEV NetfilterProtoBridge = sys.NFPROTO_BRIDGE NetfilterProtoIPv6 = sys.NFPROTO_IPV6 ) type NetfilterOptions struct { // Program must be a netfilter BPF program. Program *ebpf.Program // The protocol family. ProtocolFamily NetfilterProtocolFamily // The netfilter hook to attach to. Hook NetfilterInetHook // Priority within hook Priority int32 // Extra link flags Flags uint32 // Netfilter flags NetfilterFlags NetfilterAttachFlags } type netfilterLink struct { RawLink } // AttachNetfilter links a netfilter BPF program to a netfilter hook. func AttachNetfilter(opts NetfilterOptions) (Link, error) { if opts.Program == nil { return nil, fmt.Errorf("netfilter program is nil") } if t := opts.Program.Type(); t != ebpf.Netfilter { return nil, fmt.Errorf("invalid program type %s, expected netfilter", t) } progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } attr := sys.LinkCreateNetfilterAttr{ ProgFd: uint32(opts.Program.FD()), AttachType: sys.BPF_NETFILTER, Flags: opts.Flags, Pf: opts.ProtocolFamily, Hooknum: opts.Hook, Priority: opts.Priority, NetfilterFlags: uint32(opts.NetfilterFlags), } fd, err := sys.LinkCreateNetfilter(&attr) if err != nil { return nil, fmt.Errorf("attach netfilter link: %w", err) } return &netfilterLink{RawLink{fd, ""}}, nil } func (*netfilterLink) Update(_ *ebpf.Program) error { return fmt.Errorf("netfilter update: %w", ErrNotSupported) } func (nf *netfilterLink) Info() (*Info, error) { var info sys.NetfilterLinkInfo if err := sys.ObjInfo(nf.fd, &info); err != nil { return nil, fmt.Errorf("netfilter link info: %s", err) } extra := &NetfilterInfo{ ProtocolFamily: info.Pf, Hook: info.Hooknum, Priority: info.Priority, Flags: info.Flags, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } var _ Link = (*netfilterLink)(nil) golang-github-cilium-ebpf-0.21.0+ds1/link/netfilter_test.go000066400000000000000000000015061520243672000235200ustar00rootroot00000000000000//go:build !windows package link import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestAttachNetfilter(t *testing.T) { testutils.SkipOnOldKernel(t, "6.4", "BPF_LINK_TYPE_NETFILTER") prog := mustLoadProgram(t, ebpf.Netfilter, ebpf.AttachNetfilter, "") l, err := AttachNetfilter(NetfilterOptions{ Program: prog, ProtocolFamily: NetfilterProtoIPv4, Hook: NetfilterInetLocalOut, Priority: -128, }) if err != nil { t.Fatal(err) } info, err := l.Info() if err != nil { t.Fatal(err) } nfInfo := info.Netfilter() qt.Assert(t, qt.Equals(nfInfo.ProtocolFamily, NetfilterProtoIPv4)) qt.Assert(t, qt.Equals(nfInfo.Hook, NetfilterInetLocalOut)) qt.Assert(t, qt.Equals(nfInfo.Priority, -128)) testLink(t, l, prog) } golang-github-cilium-ebpf-0.21.0+ds1/link/netkit.go000066400000000000000000000040551520243672000217650ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "runtime" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type NetkitOptions struct { // Index of the interface to attach to. Interface int // Program to attach. Program *ebpf.Program // One of the AttachNetkit* constants. Attach ebpf.AttachType // Attach relative to an anchor. Optional. Anchor Anchor // Only attach if the expected revision matches. ExpectedRevision uint64 // Flags control the attach behaviour. Specify an Anchor instead of // F_LINK, F_ID, F_BEFORE, F_AFTER and R_REPLACE. Optional. Flags uint32 } func AttachNetkit(opts NetkitOptions) (Link, error) { if opts.Interface < 0 { return nil, fmt.Errorf("interface %d is out of bounds", opts.Interface) } if opts.Flags&anchorFlags != 0 { return nil, fmt.Errorf("disallowed flags: use Anchor to specify attach target") } attr := sys.LinkCreateNetkitAttr{ ProgFd: uint32(opts.Program.FD()), AttachType: sys.AttachType(opts.Attach), TargetIfindex: uint32(opts.Interface), ExpectedRevision: opts.ExpectedRevision, Flags: opts.Flags, } if opts.Anchor != nil { fdOrID, flags, err := opts.Anchor.anchor() if err != nil { return nil, fmt.Errorf("attach netkit link: %w", err) } attr.RelativeFdOrId = fdOrID attr.Flags |= flags } fd, err := sys.LinkCreateNetkit(&attr) runtime.KeepAlive(opts.Program) runtime.KeepAlive(opts.Anchor) if err != nil { if haveFeatErr := haveNetkit(); haveFeatErr != nil { return nil, haveFeatErr } return nil, fmt.Errorf("attach netkit link: %w", err) } return &netkitLink{RawLink{fd, ""}}, nil } type netkitLink struct { RawLink } var _ Link = (*netkitLink)(nil) func (netkit *netkitLink) Info() (*Info, error) { var info sys.NetkitLinkInfo if err := sys.ObjInfo(netkit.fd, &info); err != nil { return nil, fmt.Errorf("netkit link info: %s", err) } extra := &NetkitInfo{ Ifindex: info.Ifindex, AttachType: info.AttachType, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/netkit_test.go000066400000000000000000000055171520243672000230300ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "sync/atomic" "testing" "github.com/go-quicktest/qt" "github.com/jsimonetti/rtnetlink/v2" "github.com/jsimonetti/rtnetlink/v2/driver" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) func TestAttachNetkit(t *testing.T) { testutils.SkipOnOldKernel(t, "6.7", "Netkit Device") ns := testutils.NewNetNS(t) prog := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNetkitPrimary, "") link, _ := mustAttachNetkit(t, prog, ebpf.AttachNetkitPrimary, ns) testLink(t, link, prog) } func TestNetkitAnchor(t *testing.T) { testutils.SkipOnOldKernel(t, "6.7", "Netkit Device") a := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNetkitPrimary, "") b := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNetkitPrimary, "") ns := testutils.NewNetNS(t) linkA, ifIndex := mustAttachNetkit(t, a, ebpf.AttachNetkitPrimary, ns) programInfo, err := a.Info() qt.Assert(t, qt.IsNil(err)) programID, _ := programInfo.ID() linkInfo, err := linkA.Info() qt.Assert(t, qt.IsNil(err)) linkID := linkInfo.ID for _, anchor := range []Anchor{ Head(), Tail(), BeforeProgram(a), BeforeProgramByID(programID), AfterLink(linkA), AfterLinkByID(linkID), } { t.Run(fmt.Sprintf("%T", anchor), func(t *testing.T) { var linkB Link qt.Assert(t, qt.IsNil(ns.Do(func() (err error) { linkB, err = AttachNetkit(NetkitOptions{ Program: b, Attach: ebpf.AttachNetkitPrimary, Interface: ifIndex, Anchor: anchor, }) return err }))) qt.Assert(t, qt.IsNil(linkB.Close())) }) } } // The last ifindex we created. var prevIfindex atomic.Uint32 func init() { prevIfindex.Store(1000 - 1) } func mustAttachNetkit(tb testing.TB, prog *ebpf.Program, attachType ebpf.AttachType, ns *testutils.NetNS) (Link, int) { var conn *rtnetlink.Conn qt.Assert(tb, qt.IsNil(ns.Do(func() (err error) { conn, err = rtnetlink.Dial(nil) return err }))) tb.Cleanup(func() { qt.Assert(tb, qt.IsNil(conn.Close())) }) ifIndex := prevIfindex.Add(1) layer2 := driver.NetkitModeL2 blackhole := driver.NetkitPolicyDrop qt.Assert(tb, qt.IsNil(conn.Link.New(&rtnetlink.LinkMessage{ Family: unix.AF_UNSPEC, Index: ifIndex, Flags: unix.IFF_UP, Change: unix.IFF_UP, Attributes: &rtnetlink.LinkAttributes{ Info: &rtnetlink.LinkInfo{ Kind: "netkit", Data: &driver.Netkit{ Mode: &layer2, PeerPolicy: &blackhole, }, }, }, }))) tb.Cleanup(func() { qt.Assert(tb, qt.IsNil(conn.Link.Delete(uint32(ifIndex)))) }) var link Link qt.Assert(tb, qt.IsNil(ns.Do(func() (err error) { link, err = AttachNetkit(NetkitOptions{ Program: prog, Attach: attachType, Interface: int(ifIndex), }) return err }))) tb.Cleanup(func() { qt.Assert(tb, qt.IsNil(link.Close())) }) return link, int(ifIndex) } golang-github-cilium-ebpf-0.21.0+ds1/link/netns.go000066400000000000000000000021411520243672000216100ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) // NetNsLink is a program attached to a network namespace. type NetNsLink struct { RawLink } // AttachNetNs attaches a program to a network namespace. func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) { var attach ebpf.AttachType switch t := prog.Type(); t { case ebpf.FlowDissector: attach = ebpf.AttachFlowDissector case ebpf.SkLookup: attach = ebpf.AttachSkLookup default: return nil, fmt.Errorf("can't attach %v to network namespace", t) } link, err := AttachRawLink(RawLinkOptions{ Target: ns, Program: prog, Attach: attach, }) if err != nil { return nil, err } return &NetNsLink{*link}, nil } func (ns *NetNsLink) Info() (*Info, error) { var info sys.NetNsLinkInfo if err := sys.ObjInfo(ns.fd, &info); err != nil { return nil, fmt.Errorf("netns link info: %s", err) } extra := &NetNsInfo{ NetnsInode: info.NetnsIno, AttachType: info.AttachType, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/netns_test.go000066400000000000000000000024541520243672000226560ustar00rootroot00000000000000//go:build !windows package link import ( "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/testutils" ) func TestSkLookup(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "sk_lookup program") prog := mustLoadProgram(t, ebpf.SkLookup, ebpf.AttachSkLookup, "") netns, err := os.Open("/proc/self/ns/net") if err != nil { t.Fatal(err) } defer netns.Close() link, err := AttachNetNs(int(netns.Fd()), prog) if err != nil { t.Fatal("Can't attach link:", err) } testLink(t, link, prog) } func createSkLookupProgram() (*ebpf.Program, error) { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.SkLookup, AttachType: ebpf.AttachSkLookup, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return nil, err } return prog, nil } func ExampleAttachNetNs() { prog, err := createSkLookupProgram() if err != nil { panic(err) } defer prog.Close() // This can be a path to another netns as well. netns, err := os.Open("/proc/self/ns/net") if err != nil { panic(err) } defer netns.Close() link, err := AttachNetNs(int(netns.Fd()), prog) if err != nil { panic(err) } // The socket lookup program is now active until Close(). link.Close() } golang-github-cilium-ebpf-0.21.0+ds1/link/perf_event.go000066400000000000000000000270331520243672000226250ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "fmt" "os" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) // Getting the terminology right is usually the hardest part. For posterity and // for staying sane during implementation: // // - trace event: Representation of a kernel runtime hook. Filesystem entries // under /events. Can be tracepoints (static), kprobes or uprobes. // Can be instantiated into perf events (see below). // - tracepoint: A predetermined hook point in the kernel. Exposed as trace // events in (sub)directories under /events. Cannot be closed or // removed, they are static. // - k(ret)probe: Ephemeral trace events based on entry or exit points of // exported kernel symbols. kprobe-based (tracefs) trace events can be // created system-wide by writing to the /kprobe_events file, or // they can be scoped to the current process by creating PMU perf events. // - u(ret)probe: Ephemeral trace events based on user provides ELF binaries // and offsets. uprobe-based (tracefs) trace events can be // created system-wide by writing to the /uprobe_events file, or // they can be scoped to the current process by creating PMU perf events. // - perf event: An object instantiated based on an existing trace event or // kernel symbol. Referred to by fd in userspace. // Exactly one eBPF program can be attached to a perf event. Multiple perf // events can be created from a single trace event. Closing a perf event // stops any further invocations of the attached eBPF program. var ( errInvalidInput = tracefs.ErrInvalidInput ) const ( perfAllThreads = -1 ) // A perfEvent represents a perf event kernel object. Exactly one eBPF program // can be attached to it. It is created based on a tracefs trace event or a // Performance Monitoring Unit (PMU). type perfEvent struct { // Trace event backing this perfEvent. May be nil. tracefsEvent *tracefs.Event // This is the perf event FD. fd *sys.FD } func newPerfEvent(fd *sys.FD, event *tracefs.Event) *perfEvent { pe := &perfEvent{event, fd} return pe } func (pe *perfEvent) Close() error { // We close the perf event before attempting to remove the tracefs event. if err := pe.fd.Close(); err != nil { return fmt.Errorf("closing perf event fd: %w", err) } if pe.tracefsEvent != nil { return pe.tracefsEvent.Close() } return nil } // PerfEvent is implemented by some Link types which use a perf event under // the hood. type PerfEvent interface { // PerfEvent returns a file for the underlying perf event. // // It is the callers responsibility to close the returned file. // // Making changes to the associated perf event lead to // undefined behaviour. PerfEvent() (*os.File, error) } // perfEventLink represents a bpf perf link. type perfEventLink struct { RawLink pe *perfEvent } func (pl *perfEventLink) isLink() {} func (pl *perfEventLink) Close() error { if err := pl.fd.Close(); err != nil { return fmt.Errorf("perf link close: %w", err) } // when created from pinned link if pl.pe == nil { return nil } if err := pl.pe.Close(); err != nil { return fmt.Errorf("perf event close: %w", err) } return nil } func (pl *perfEventLink) Update(_ *ebpf.Program) error { return fmt.Errorf("perf event link update: %w", ErrNotSupported) } var _ PerfEvent = (*perfEventLink)(nil) func (pl *perfEventLink) PerfEvent() (*os.File, error) { // when created from pinned link if pl.pe == nil { return nil, ErrNotSupported } fd, err := pl.pe.fd.Dup() if err != nil { return nil, err } return fd.File("perf-event") } // queryInfoWithString queries object info that contains a string field. // // The passed stringField and stringLengthField must point to the string field // and its length field inside the info struct respectively. // // It returns the queried string and fills in the passed info struct. func queryInfoWithString(fd *sys.FD, info sys.Info, stringField *sys.TypedPointer[byte], stringLengthField *uint32) (string, error) { // Query info to get the length if err := sys.ObjInfo(fd, info); err != nil { return "", err } // The stringLengthField pointer points to a field inside info, so it is now populated. var stringData = make([]byte, *stringLengthField) *stringField = sys.SlicePointer(stringData) // Query info again to fill in the string. // Since the stringField pointer points to a field inside info, // the info now contains the pointer to our allocated stringData. if err := sys.ObjInfo(fd, info); err != nil { return "", fmt.Errorf("object info with string: %s", err) } return unix.ByteSliceToString(stringData), nil } func (pl *perfEventLink) Info() (*Info, error) { var info sys.PerfEventLinkInfo if err := sys.ObjInfo(pl.fd, &info); err != nil { return nil, fmt.Errorf("perf event link info: %s", err) } var extra2 interface{} switch info.PerfEventType { case PerfEventKprobe, PerfEventKretprobe: var kprobeInfo sys.KprobeLinkInfo funcName, err := queryInfoWithString(pl.fd, &kprobeInfo, &kprobeInfo.FuncName, &kprobeInfo.NameLen) if err != nil { return nil, fmt.Errorf("kprobe link info: %s", err) } extra2 = &KprobeInfo{ Address: kprobeInfo.Addr, Missed: kprobeInfo.Missed, Function: funcName, Offset: kprobeInfo.Offset, } case PerfEventUprobe, PerfEventUretprobe: var uprobeInfo sys.UprobeLinkInfo fileName, err := queryInfoWithString(pl.fd, &uprobeInfo, &uprobeInfo.FileName, &uprobeInfo.NameLen) if err != nil { return nil, fmt.Errorf("uprobe link info: %s", err) } extra2 = &UprobeInfo{ Offset: uprobeInfo.Offset, Cookie: uprobeInfo.Cookie, OffsetReferenceCount: uprobeInfo.RefCtrOffset, File: fileName, } case PerfEventTracepoint: var tracepointInfo sys.TracepointLinkInfo tpName, err := queryInfoWithString(pl.fd, &tracepointInfo, &tracepointInfo.TpName, &tracepointInfo.NameLen) if err != nil { return nil, fmt.Errorf("perf event link info: %w", err) } extra2 = &TracepointInfo{ Tracepoint: tpName, Cookie: tracepointInfo.Cookie, } case PerfEventEvent: var eventInfo sys.EventLinkInfo err := sys.ObjInfo(pl.fd, &eventInfo) if err != nil { return nil, fmt.Errorf("trace point link info: %s", err) } extra2 = &EventInfo{ Config: eventInfo.Config, Type: eventInfo.EventType, Cookie: eventInfo.Cookie, } } extra := &PerfEventInfo{ Type: info.PerfEventType, extra: extra2, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } // perfEventIoctl implements Link and handles the perf event lifecycle // via ioctl(). type perfEventIoctl struct { *perfEvent } func (pi *perfEventIoctl) isLink() {} // Since 4.15 (e87c6bc3852b "bpf: permit multiple bpf attachments for a single perf event"), // calling PERF_EVENT_IOC_SET_BPF appends the given program to a prog_array // owned by the perf event, which means multiple programs can be attached // simultaneously. // // Before 4.15, calling PERF_EVENT_IOC_SET_BPF more than once on a perf event // returns EEXIST. // // Detaching a program from a perf event is currently not possible, so a // program replacement mechanism cannot be implemented for perf events. func (pi *perfEventIoctl) Update(_ *ebpf.Program) error { return fmt.Errorf("perf event ioctl update: %w", ErrNotSupported) } func (pi *perfEventIoctl) Pin(string) error { return fmt.Errorf("perf event ioctl pin: %w", ErrNotSupported) } func (pi *perfEventIoctl) Unpin() error { return fmt.Errorf("perf event ioctl unpin: %w", ErrNotSupported) } func (pi *perfEventIoctl) Detach() error { return fmt.Errorf("perf event ioctl detach: %w", ErrNotSupported) } func (pi *perfEventIoctl) Info() (*Info, error) { return nil, fmt.Errorf("perf event ioctl info: %w", ErrNotSupported) } var _ PerfEvent = (*perfEventIoctl)(nil) func (pi *perfEventIoctl) PerfEvent() (*os.File, error) { fd, err := pi.fd.Dup() if err != nil { return nil, err } return fd.File("perf-event") } // attach the given eBPF prog to the perf event stored in pe. // pe must contain a valid perf event fd. // prog's type must match the program type stored in pe. func attachPerfEvent(pe *perfEvent, prog *ebpf.Program, cookie uint64) (Link, error) { if prog == nil { return nil, errors.New("cannot attach a nil program") } if prog.FD() < 0 { return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd) } if err := haveBPFLinkPerfEvent(); err == nil { return attachPerfEventLink(pe, prog, cookie) } if cookie != 0 { return nil, fmt.Errorf("cookies are not supported: %w", ErrNotSupported) } return attachPerfEventIoctl(pe, prog) } func attachPerfEventIoctl(pe *perfEvent, prog *ebpf.Program) (*perfEventIoctl, error) { // Assign the eBPF program to the perf event. err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_SET_BPF, prog.FD()) if err != nil { return nil, fmt.Errorf("setting perf event bpf program: %w", err) } // PERF_EVENT_IOC_ENABLE and _DISABLE ignore their given values. if err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil { return nil, fmt.Errorf("enable perf event: %s", err) } return &perfEventIoctl{pe}, nil } // Use the bpf api to attach the perf event (BPF_LINK_TYPE_PERF_EVENT, 5.15+). // // https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e func attachPerfEventLink(pe *perfEvent, prog *ebpf.Program, cookie uint64) (*perfEventLink, error) { fd, err := sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{ ProgFd: uint32(prog.FD()), TargetFd: pe.fd.Uint(), AttachType: sys.BPF_PERF_EVENT, BpfCookie: cookie, }) if err != nil { return nil, fmt.Errorf("cannot create bpf perf link: %v", err) } return &perfEventLink{RawLink{fd: fd}, pe}, nil } // unsafeStringPtr returns an unsafe.Pointer to a NUL-terminated copy of str. func unsafeStringPtr(str string) (unsafe.Pointer, error) { p, err := unix.BytePtrFromString(str) if err != nil { return nil, err } return unsafe.Pointer(p), nil } // openTracepointPerfEvent opens a tracepoint-type perf event. System-wide // [k,u]probes created by writing to /[k,u]probe_events are tracepoints // behind the scenes, and can be attached to using these perf events. func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) { attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_TRACEPOINT, Config: tid, Sample_type: unix.PERF_SAMPLE_RAW, Sample: 1, Wakeup: 1, } cpu := 0 if pid != perfAllThreads { cpu = -1 } fd, err := unix.PerfEventOpen(&attr, pid, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC) if err != nil { return nil, fmt.Errorf("opening tracepoint perf event: %w", err) } return sys.NewFD(fd) } // Probe BPF perf link. // // https://elixir.bootlin.com/linux/v5.16.8/source/kernel/bpf/syscall.c#L4307 // https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "probe_bpf_perf_link", Type: ebpf.Kprobe, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }) if err != nil { return err } defer prog.Close() _, err = sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{ ProgFd: uint32(prog.FD()), AttachType: sys.BPF_PERF_EVENT, }) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }, "5.15") golang-github-cilium-ebpf-0.21.0+ds1/link/perf_event_test.go000066400000000000000000000003151520243672000236560ustar00rootroot00000000000000//go:build !windows package link import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveBPFLinkPerfEvent(t *testing.T) { testutils.CheckFeatureTest(t, haveBPFLinkPerfEvent) } golang-github-cilium-ebpf-0.21.0+ds1/link/program.go000066400000000000000000000055171520243672000221420ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "runtime" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type RawAttachProgramOptions struct { // Target to query. This is usually a file descriptor but may refer to // something else based on the attach type. Target int // Program to attach. Program *ebpf.Program // Attach must match the attach type of Program. Attach ebpf.AttachType // Attach relative to an anchor. Optional. Anchor Anchor // Flags control the attach behaviour. Specify an Anchor instead of // F_LINK, F_ID, F_BEFORE, F_AFTER and F_REPLACE. Optional. Flags uint32 // Only attach if the internal revision matches the given value. ExpectedRevision uint64 } // RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH. // // You should use one of the higher level abstractions available in this // package if possible. func RawAttachProgram(opts RawAttachProgramOptions) error { if opts.Flags&anchorFlags != 0 { return fmt.Errorf("disallowed flags: use Anchor to specify attach target") } attr := sys.ProgAttachAttr{ TargetFdOrIfindex: uint32(opts.Target), AttachBpfFd: uint32(opts.Program.FD()), AttachType: uint32(opts.Attach), AttachFlags: uint32(opts.Flags), ExpectedRevision: opts.ExpectedRevision, } if opts.Anchor != nil { fdOrID, flags, err := opts.Anchor.anchor() if err != nil { return fmt.Errorf("attach program: %w", err) } if flags == sys.BPF_F_REPLACE { // Ensure that replacing a program works on old kernels. attr.ReplaceBpfFd = fdOrID } else { attr.RelativeFdOrId = fdOrID attr.AttachFlags |= flags } } if err := sys.ProgAttach(&attr); err != nil { if haveFeatErr := haveProgAttach(); haveFeatErr != nil { return haveFeatErr } return fmt.Errorf("attach program: %w", err) } runtime.KeepAlive(opts.Program) return nil } type RawDetachProgramOptions RawAttachProgramOptions // RawDetachProgram is a low level wrapper around BPF_PROG_DETACH. // // You should use one of the higher level abstractions available in this // package if possible. func RawDetachProgram(opts RawDetachProgramOptions) error { if opts.Flags&anchorFlags != 0 { return fmt.Errorf("disallowed flags: use Anchor to specify attach target") } attr := sys.ProgDetachAttr{ TargetFdOrIfindex: uint32(opts.Target), AttachBpfFd: uint32(opts.Program.FD()), AttachType: uint32(opts.Attach), ExpectedRevision: opts.ExpectedRevision, } if opts.Anchor != nil { fdOrID, flags, err := opts.Anchor.anchor() if err != nil { return fmt.Errorf("detach program: %w", err) } attr.RelativeFdOrId = fdOrID attr.AttachFlags |= flags } if err := sys.ProgDetach(&attr); err != nil { if haveFeatErr := haveProgAttach(); haveFeatErr != nil { return haveFeatErr } return fmt.Errorf("can't detach program: %w", err) } return nil } golang-github-cilium-ebpf-0.21.0+ds1/link/program_test.go000066400000000000000000000056141520243672000231770ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "net" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" ) func TestProgramAlter(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "SkSKB type") prog := mustLoadProgram(t, ebpf.SkSKB, 0, "") var sockMap *ebpf.Map sockMap, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.MapType(15), // BPF_MAP_TYPE_SOCKMAP KeySize: 4, ValueSize: 4, MaxEntries: 2, }) if err != nil { t.Fatal(err) } defer sockMap.Close() err = RawAttachProgram(RawAttachProgramOptions{ Target: sockMap.FD(), Program: prog, Attach: ebpf.AttachSkSKBStreamParser, }) if err != nil { t.Fatal(err) } err = RawDetachProgram(RawDetachProgramOptions{ Target: sockMap.FD(), Program: prog, Attach: ebpf.AttachSkSKBStreamParser, }) if err != nil { t.Fatal(err) } } func TestRawAttachProgramAnchor(t *testing.T) { testutils.SkipOnOldKernel(t, "6.6", "attach anchor") iface, err := net.InterfaceByName("lo") qt.Assert(t, qt.IsNil(err)) a := mustLoadProgram(t, ebpf.SchedCLS, 0, "") info, err := a.Info() qt.Assert(t, qt.IsNil(err)) aID, _ := info.ID() err = RawAttachProgram(RawAttachProgramOptions{ Target: iface.Index, Program: a, Attach: ebpf.AttachTCXIngress, }) qt.Assert(t, qt.IsNil(err)) defer RawDetachProgram(RawDetachProgramOptions{ Target: iface.Index, Program: a, Attach: ebpf.AttachTCXIngress, }) link, err := AttachTCX(TCXOptions{ Interface: iface.Index, Program: mustLoadProgram(t, ebpf.SchedCLS, 0, ""), Attach: ebpf.AttachTCXIngress, }) qt.Assert(t, qt.IsNil(err)) defer link.Close() linkInfo, err := link.Info() qt.Assert(t, qt.IsNil(err)) b := mustLoadProgram(t, ebpf.SchedCLS, 0, "") for _, anchor := range []Anchor{ Head(), Tail(), AfterProgram(a), AfterProgramByID(aID), AfterLink(link), AfterLinkByID(linkInfo.ID), } { t.Run(fmt.Sprintf("%T", anchor), func(t *testing.T) { err := RawAttachProgram(RawAttachProgramOptions{ Target: iface.Index, Program: b, Attach: ebpf.AttachTCXIngress, Anchor: anchor, }) qt.Assert(t, qt.IsNil(err)) // Detach doesn't allow first or last anchor. if _, ok := anchor.(firstAnchor); ok { anchor = nil } else if _, ok := anchor.(lastAnchor); ok { anchor = nil } err = RawDetachProgram(RawDetachProgramOptions{ Target: iface.Index, Program: b, Attach: ebpf.AttachTCXIngress, Anchor: anchor, }) qt.Assert(t, qt.IsNil(err)) }) } // Check that legacy replacement with a program works. err = RawAttachProgram(RawAttachProgramOptions{ Target: iface.Index, Program: b, Attach: ebpf.AttachTCXIngress, Anchor: ReplaceProgram(a), }) qt.Assert(t, qt.IsNil(err)) err = RawDetachProgram(RawDetachProgramOptions{ Target: iface.Index, Program: b, Attach: ebpf.AttachTCXIngress, }) qt.Assert(t, qt.IsNil(err)) } golang-github-cilium-ebpf-0.21.0+ds1/link/query.go000066400000000000000000000064401520243672000216340ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "slices" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) // QueryOptions defines additional parameters when querying for programs. type QueryOptions struct { // Target to query. This is usually a file descriptor but may refer to // something else based on the attach type. Target int // Attach specifies the AttachType of the programs queried for Attach ebpf.AttachType // QueryFlags are flags for BPF_PROG_QUERY, e.g. BPF_F_QUERY_EFFECTIVE QueryFlags uint32 } // QueryResult describes which programs and links are active. type QueryResult struct { // List of attached programs. Programs []AttachedProgram // Incremented by one every time the set of attached programs changes. // May be zero if not supported by the [ebpf.AttachType]. Revision uint64 } // HaveLinkInfo returns true if the kernel supports querying link information // for a particular [ebpf.AttachType]. func (qr *QueryResult) HaveLinkInfo() bool { return slices.ContainsFunc(qr.Programs, func(ap AttachedProgram) bool { _, ok := ap.LinkID() return ok }, ) } type AttachedProgram struct { ID ebpf.ProgramID linkID ID } // LinkID returns the ID associated with the program. // // Returns 0, false if the kernel doesn't support retrieving the ID or if the // program wasn't attached via a link. func (ap *AttachedProgram) LinkID() (ID, bool) { return ap.linkID, ap.linkID != 0 } // QueryPrograms retrieves a list of programs for the given AttachType. // // Returns a slice of attached programs, which may be empty. // revision counts how many times the set of attached programs has changed and // may be zero if not supported by the [ebpf.AttachType]. // Returns ErrNotSupportd on a kernel without BPF_PROG_QUERY func QueryPrograms(opts QueryOptions) (*QueryResult, error) { // query the number of programs to allocate correct slice size attr := sys.ProgQueryAttr{ TargetFdOrIfindex: uint32(opts.Target), AttachType: sys.AttachType(opts.Attach), QueryFlags: opts.QueryFlags, } err := sys.ProgQuery(&attr) if err != nil { if haveFeatErr := haveProgQuery(); haveFeatErr != nil { return nil, fmt.Errorf("query programs: %w", haveFeatErr) } return nil, fmt.Errorf("query programs: %w", err) } if attr.Count == 0 { return &QueryResult{Revision: attr.Revision}, nil } // The minimum bpf_mprog revision is 1, so we can use the field to detect // whether the attach type supports link ids. haveLinkIDs := attr.Revision != 0 count := attr.Count progIds := make([]ebpf.ProgramID, count) attr = sys.ProgQueryAttr{ TargetFdOrIfindex: uint32(opts.Target), AttachType: sys.AttachType(opts.Attach), QueryFlags: opts.QueryFlags, Count: count, ProgIds: sys.SlicePointer(progIds), } var linkIds []ID if haveLinkIDs { linkIds = make([]ID, count) attr.LinkIds = sys.SlicePointer(linkIds) } if err := sys.ProgQuery(&attr); err != nil { return nil, fmt.Errorf("query programs: %w", err) } // NB: attr.Count might have changed between the two syscalls. var programs []AttachedProgram for i, id := range progIds[:attr.Count] { ap := AttachedProgram{ID: id} if haveLinkIDs { ap.linkID = linkIds[i] } programs = append(programs, ap) } return &QueryResult{programs, attr.Revision}, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/query_test.go000066400000000000000000000065331520243672000226760ustar00rootroot00000000000000//go:build !windows package link import ( "os" "slices" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" ) func TestQueryPrograms(t *testing.T) { for name, fn := range map[string]func(*testing.T) (*ebpf.Program, Link, QueryOptions){ "cgroup": queryCgroupProgAttachFixtures, "cgroup link": queryCgroupLinkFixtures, "netns": queryNetNSFixtures, "tcx": queryTCXFixtures, } { t.Run(name, func(t *testing.T) { prog, link, opts := fn(t) result, err := QueryPrograms(opts) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) progInfo, err := prog.Info() qt.Assert(t, qt.IsNil(err)) progID, _ := progInfo.ID() i := slices.IndexFunc(result.Programs, func(ap AttachedProgram) bool { return ap.ID == progID }) qt.Assert(t, qt.Not(qt.Equals(i, -1))) if name == "tcx" { qt.Assert(t, qt.Not(qt.Equals(result.Revision, 0))) } if result.HaveLinkInfo() { ap := result.Programs[i] linkInfo, err := link.Info() qt.Assert(t, qt.IsNil(err)) linkID, ok := ap.LinkID() qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(linkID, linkInfo.ID)) } }) } } func queryCgroupProgAttachFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) { cgroup, prog := mustCgroupFixtures(t) link, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, flagAllowOverride) if err != nil { t.Fatal("Can't create link:", err) } t.Cleanup(func() { qt.Assert(t, qt.IsNil(link.Close())) }) return prog, nil, QueryOptions{ Target: int(cgroup.Fd()), Attach: ebpf.AttachCGroupInetEgress, } } func queryCgroupLinkFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) { cgroup, prog := mustCgroupFixtures(t) link, err := newLinkCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create link:", err) } t.Cleanup(func() { qt.Assert(t, qt.IsNil(link.Close())) }) return prog, nil, QueryOptions{ Target: int(cgroup.Fd()), Attach: ebpf.AttachCGroupInetEgress, } } func queryNetNSFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) { testutils.SkipOnOldKernel(t, "4.20", "flow_dissector program") prog := mustLoadProgram(t, ebpf.FlowDissector, ebpf.AttachFlowDissector, "") // RawAttachProgramOptions.Target needs to be 0, as PROG_ATTACH with namespaces // only works with the threads current netns. Any other fd will be rejected. if err := RawAttachProgram(RawAttachProgramOptions{ Target: 0, Program: prog, Attach: ebpf.AttachFlowDissector, }); err != nil { t.Fatal(err) } t.Cleanup(func() { err := RawDetachProgram(RawDetachProgramOptions{ Target: 0, Program: prog, Attach: ebpf.AttachFlowDissector, }) if err != nil { t.Fatal(err) } }) netns, err := os.Open("/proc/self/ns/net") qt.Assert(t, qt.IsNil(err)) t.Cleanup(func() { netns.Close() }) return prog, nil, QueryOptions{ Target: int(netns.Fd()), Attach: ebpf.AttachFlowDissector, } } func queryTCXFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) { testutils.SkipOnOldKernel(t, "6.6", "TCX link") prog := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachTCXIngress, "") link, iface := mustAttachTCX(t, prog, ebpf.AttachTCXIngress) return prog, link, QueryOptions{ Target: iface, Attach: ebpf.AttachTCXIngress, } } golang-github-cilium-ebpf-0.21.0+ds1/link/raw_tracepoint.go000066400000000000000000000050101520243672000235000ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type RawTracepointOptions struct { // Tracepoint name. Name string // Program must be of type RawTracepoint* Program *ebpf.Program } // AttachRawTracepoint links a BPF program to a raw_tracepoint. // // Requires at least Linux 4.17. func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.RawTracepoint && t != ebpf.RawTracepointWritable { return nil, fmt.Errorf("invalid program type %s, expected RawTracepoint(Writable)", t) } if opts.Program.FD() < 0 { return nil, fmt.Errorf("invalid program: %w", sys.ErrClosedFd) } fd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{ Name: sys.NewStringPointer(opts.Name), ProgFd: uint32(opts.Program.FD()), }) if err != nil { return nil, err } err = haveBPFLink() if errors.Is(err, ErrNotSupported) { // Prior to commit 70ed506c3bbc ("bpf: Introduce pinnable bpf_link abstraction") // raw_tracepoints are just a plain fd. return &simpleRawTracepoint{fd}, nil } if err != nil { return nil, err } return &rawTracepoint{RawLink{fd: fd}}, nil } type simpleRawTracepoint struct { fd *sys.FD } var _ Link = (*simpleRawTracepoint)(nil) func (frt *simpleRawTracepoint) isLink() {} func (frt *simpleRawTracepoint) Close() error { return frt.fd.Close() } func (frt *simpleRawTracepoint) Update(_ *ebpf.Program) error { return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported) } func (frt *simpleRawTracepoint) Pin(string) error { return fmt.Errorf("pin raw_tracepoint: %w", ErrNotSupported) } func (frt *simpleRawTracepoint) Unpin() error { return fmt.Errorf("unpin raw_tracepoint: %w", ErrNotSupported) } func (frt *simpleRawTracepoint) Detach() error { return fmt.Errorf("detach raw_tracepoint: %w", ErrNotSupported) } func (frt *simpleRawTracepoint) Info() (*Info, error) { return nil, fmt.Errorf("can't get raw_tracepoint info: %w", ErrNotSupported) } type rawTracepoint struct { RawLink } var _ Link = (*rawTracepoint)(nil) func (rt *rawTracepoint) Update(_ *ebpf.Program) error { return fmt.Errorf("update raw_tracepoint: %w", ErrNotSupported) } func (rt *rawTracepoint) Info() (*Info, error) { var info sys.RawTracepointLinkInfo name, err := queryInfoWithString(rt.fd, &info, &info.TpName, &info.TpNameLen) if err != nil { return nil, err } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), &RawTracepointInfo{ Name: name, }, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/raw_tracepoint_test.go000066400000000000000000000025541520243672000245510ustar00rootroot00000000000000//go:build !windows package link import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestRawTracepoint(t *testing.T) { testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API") prog := mustLoadProgram(t, ebpf.RawTracepoint, 0, "") link, err := AttachRawTracepoint(RawTracepointOptions{ Name: "cgroup_mkdir", Program: prog, }) if err != nil { t.Fatal(err) } testLink(t, link, prog) } func TestRawTracepointInfo(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "bpf_link_info_raw_tracepoint") prog := mustLoadProgram(t, ebpf.RawTracepoint, 0, "") link, err := AttachRawTracepoint(RawTracepointOptions{ Name: "cgroup_mkdir", Program: prog, }) if err != nil { t.Fatal(err) } defer link.Close() info, err := link.Info() if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(RawTracepointType, info.Type)) tpInfo := info.RawTracepoint() qt.Assert(t, qt.Equals(tpInfo.Name, "cgroup_mkdir")) } func TestRawTracepoint_writable(t *testing.T) { testutils.SkipOnOldKernel(t, "5.2", "BPF_RAW_TRACEPOINT_WRITABLE API") prog := mustLoadProgram(t, ebpf.RawTracepoint, 0, "") defer prog.Close() link, err := AttachRawTracepoint(RawTracepointOptions{ Name: "cgroup_rmdir", Program: prog, }) if err != nil { t.Fatal(err) } testLink(t, link, prog) } golang-github-cilium-ebpf-0.21.0+ds1/link/socket_filter.go000066400000000000000000000016501520243672000233220ustar00rootroot00000000000000//go:build !windows package link import ( "syscall" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/unix" ) // AttachSocketFilter attaches a SocketFilter BPF program to a socket. func AttachSocketFilter(conn syscall.Conn, program *ebpf.Program) error { rawConn, err := conn.SyscallConn() if err != nil { return err } var ssoErr error err = rawConn.Control(func(fd uintptr) { ssoErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_ATTACH_BPF, program.FD()) }) if ssoErr != nil { return ssoErr } return err } // DetachSocketFilter detaches a SocketFilter BPF program from a socket. func DetachSocketFilter(conn syscall.Conn) error { rawConn, err := conn.SyscallConn() if err != nil { return err } var ssoErr error err = rawConn.Control(func(fd uintptr) { ssoErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_DETACH_BPF, 0) }) if ssoErr != nil { return ssoErr } return err } golang-github-cilium-ebpf-0.21.0+ds1/link/socket_filter_test.go000066400000000000000000000007531520243672000243640ustar00rootroot00000000000000//go:build !windows package link import ( "net" "testing" "github.com/cilium/ebpf" ) func TestSocketFilterAttach(t *testing.T) { prog := mustLoadProgram(t, ebpf.SocketFilter, 0, "") defer prog.Close() conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) if err != nil { t.Fatal(err) } defer conn.Close() if err := AttachSocketFilter(conn, prog); err != nil { t.Fatal(err) } if err := DetachSocketFilter(conn); err != nil { t.Fatal(err) } } golang-github-cilium-ebpf-0.21.0+ds1/link/struct_ops.go000066400000000000000000000021611520243672000226700ustar00rootroot00000000000000package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type structOpsLink struct { RawLink } func (*structOpsLink) Update(*ebpf.Program) error { return fmt.Errorf("update struct_ops link: %w", ErrNotSupported) } type StructOpsOptions struct { Map *ebpf.Map } // AttachStructOps attaches a struct_ops map (created from a ".struct_ops.link" // section) to its kernel subsystem via a BPF link. func AttachStructOps(opts StructOpsOptions) (Link, error) { m := opts.Map if m == nil { return nil, fmt.Errorf("map cannot be nil") } if t := m.Type(); t != ebpf.StructOpsMap { return nil, fmt.Errorf("can't attach non-struct_ops map") } mapFD := m.FD() if mapFD <= 0 { return nil, fmt.Errorf("invalid map: %s", sys.ErrClosedFd) } fd, err := sys.LinkCreate(&sys.LinkCreateAttr{ // For struct_ops links, the mapFD must be passed as ProgFd. ProgFd: uint32(mapFD), AttachType: sys.AttachType(ebpf.AttachStructOps), TargetFd: 0, }) if err != nil { return nil, fmt.Errorf("attach StructOps: create link: %w", err) } return &structOpsLink{RawLink{fd: fd}}, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/struct_ops_test.go000066400000000000000000000051771520243672000237410ustar00rootroot00000000000000//go:build !windows package link import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" ) func TestStructOps(t *testing.T) { testutils.SkipOnOldKernel(t, "6.12", "bpf_testmod_ops") m := mustStructOpsFixtures(t) l, err := AttachStructOps(StructOpsOptions{Map: m}) qt.Assert(t, qt.IsNil(err)) testLink(t, l, nil) } func mustStructOpsFixtures(tb testing.TB) *ebpf.Map { tb.Helper() testutils.SkipIfNotSupported(tb, haveBPFLink()) userData := []byte{ // test_1 func ptr (8B) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // test_2 func ptr (8B) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // data (4B) + padding (4B) 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, } spec := &ebpf.CollectionSpec{ Maps: map[string]*ebpf.MapSpec{ "testmod_ops": { Name: "testmod_ops", Type: ebpf.StructOpsMap, MaxEntries: 1, Flags: sys.BPF_F_LINK, Key: &btf.Int{Size: 4}, KeySize: 4, ValueSize: 24, Value: &btf.Struct{ Name: "bpf_testmod_ops", Size: 24, Members: []btf.Member{ { Name: "test_1", Type: &btf.Pointer{ Target: &btf.FuncProto{ Params: []btf.FuncParam{}, Return: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}}}, Offset: 0, }, { Name: "test_2", Type: &btf.Pointer{ Target: &btf.FuncProto{ Params: []btf.FuncParam{ {Type: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}}, {Type: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}}, }, Return: (*btf.Void)(nil), }, }, Offset: 64, }, { Name: "data", Type: &btf.Int{Name: "int", Size: 4, Encoding: btf.Signed}, Offset: 128, // bits }, }, }, Contents: []ebpf.MapKV{ { Key: uint32(0), Value: userData, }, }, }, }, Programs: map[string]*ebpf.ProgramSpec{ "test_1": { Name: "test_1", Type: ebpf.StructOps, AttachTo: "bpf_testmod_ops:test_1", License: "GPL", SectionName: "struct_ops/test_1", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }, }, Variables: map[string]*ebpf.VariableSpec{}, } coll, err := ebpf.NewCollection(spec) testutils.SkipIfNotSupported(tb, err) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { coll.Close() }) m := coll.Maps["testmod_ops"] qt.Assert(tb, qt.IsNotNil(m)) return m } golang-github-cilium-ebpf-0.21.0+ds1/link/syscalls.go000066400000000000000000000106641520243672000223270ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSKB, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return internal.ErrNotSupported } // BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB, // so being able to load the program is enough to infer that we // have the syscall. prog.Close() return nil }, "4.10") var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic replacement of MULTI progs", func() error { if err := haveProgAttach(); err != nil { return err } prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.CGroupSKB, AttachType: ebpf.AttachCGroupInetIngress, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return internal.ErrNotSupported } defer prog.Close() // We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs. // If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't // present. attr := sys.ProgAttachAttr{ // We rely on this being checked after attachFlags. TargetFdOrIfindex: ^uint32(0), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(ebpf.AttachCGroupInetIngress), AttachFlags: uint32(flagReplace), } err = sys.ProgAttach(&attr) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }, "5.5") var haveBPFLink = internal.NewFeatureTest("bpf_link", func() error { attr := sys.LinkCreateAttr{ // This is a hopefully invalid file descriptor, which triggers EBADF. TargetFd: ^uint32(0), ProgFd: ^uint32(0), AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress), } _, err := sys.LinkCreate(&attr) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }, "5.7") var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", func() error { attr := sys.ProgQueryAttr{ // We rely on this being checked during the syscall. // With an otherwise correct payload we expect EBADF here // as an indication that the feature is present. TargetFdOrIfindex: ^uint32(0), AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress), } err := sys.ProgQuery(&attr) if errors.Is(err, unix.EBADF) { return nil } if err != nil { return ErrNotSupported } return errors.New("syscall succeeded unexpectedly") }, "4.15") var haveTCX = internal.NewFeatureTest("tcx", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.SchedCLS, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return internal.ErrNotSupported } defer prog.Close() attr := sys.LinkCreateTcxAttr{ // We rely on this being checked during the syscall. // With an otherwise correct payload we expect ENODEV here // as an indication that the feature is present. TargetIfindex: ^uint32(0), ProgFd: uint32(prog.FD()), AttachType: sys.AttachType(ebpf.AttachTCXIngress), } _, err = sys.LinkCreateTcx(&attr) if errors.Is(err, unix.ENODEV) { return nil } if err != nil { return ErrNotSupported } return errors.New("syscall succeeded unexpectedly") }, "6.6") var haveNetkit = internal.NewFeatureTest("netkit", func() error { prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Type: ebpf.SchedCLS, License: "MIT", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }) if err != nil { return internal.ErrNotSupported } defer prog.Close() attr := sys.LinkCreateNetkitAttr{ // We rely on this being checked during the syscall. // With an otherwise correct payload we expect ENODEV here // as an indication that the feature is present. TargetIfindex: ^uint32(0), ProgFd: uint32(prog.FD()), AttachType: sys.AttachType(ebpf.AttachNetkitPrimary), } _, err = sys.LinkCreateNetkit(&attr) if errors.Is(err, unix.ENODEV) { return nil } if err != nil { return ErrNotSupported } return errors.New("syscall succeeded unexpectedly") }, "6.7") golang-github-cilium-ebpf-0.21.0+ds1/link/syscalls_test.go000066400000000000000000000011631520243672000233600ustar00rootroot00000000000000//go:build !windows package link import ( "testing" "github.com/cilium/ebpf/internal/testutils" ) func TestHaveProgAttach(t *testing.T) { testutils.CheckFeatureTest(t, haveProgAttach) } func TestHaveProgAttachReplace(t *testing.T) { testutils.CheckFeatureTest(t, haveProgAttachReplace) } func TestHaveBPFLink(t *testing.T) { testutils.CheckFeatureTest(t, haveBPFLink) } func TestHaveProgQuery(t *testing.T) { testutils.CheckFeatureTest(t, haveProgQuery) } func TestHaveTCX(t *testing.T) { testutils.CheckFeatureTest(t, haveTCX) } func TestHaveNetkit(t *testing.T) { testutils.CheckFeatureTest(t, haveNetkit) } golang-github-cilium-ebpf-0.21.0+ds1/link/tcx.go000066400000000000000000000037671520243672000212760ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "runtime" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) type TCXOptions struct { // Index of the interface to attach to. Interface int // Program to attach. Program *ebpf.Program // One of the AttachTCX* constants. Attach ebpf.AttachType // Attach relative to an anchor. Optional. Anchor Anchor // Only attach if the expected revision matches. ExpectedRevision uint64 // Flags control the attach behaviour. Specify an Anchor instead of // F_LINK, F_ID, F_BEFORE, F_AFTER and R_REPLACE. Optional. Flags uint32 } func AttachTCX(opts TCXOptions) (Link, error) { if opts.Interface < 0 { return nil, fmt.Errorf("interface %d is out of bounds", opts.Interface) } if opts.Flags&anchorFlags != 0 { return nil, fmt.Errorf("disallowed flags: use Anchor to specify attach target") } attr := sys.LinkCreateTcxAttr{ ProgFd: uint32(opts.Program.FD()), AttachType: sys.AttachType(opts.Attach), TargetIfindex: uint32(opts.Interface), ExpectedRevision: opts.ExpectedRevision, Flags: opts.Flags, } if opts.Anchor != nil { fdOrID, flags, err := opts.Anchor.anchor() if err != nil { return nil, fmt.Errorf("attach tcx link: %w", err) } attr.RelativeFdOrId = fdOrID attr.Flags |= flags } fd, err := sys.LinkCreateTcx(&attr) runtime.KeepAlive(opts.Program) runtime.KeepAlive(opts.Anchor) if err != nil { if haveFeatErr := haveTCX(); haveFeatErr != nil { return nil, haveFeatErr } return nil, fmt.Errorf("attach tcx link: %w", err) } return &tcxLink{RawLink{fd, ""}}, nil } type tcxLink struct { RawLink } var _ Link = (*tcxLink)(nil) func (tcx *tcxLink) Info() (*Info, error) { var info sys.TcxLinkInfo if err := sys.ObjInfo(tcx.fd, &info); err != nil { return nil, fmt.Errorf("tcx link info: %s", err) } extra := &TCXInfo{ Ifindex: info.Ifindex, AttachType: info.AttachType, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/tcx_test.go000066400000000000000000000042041520243672000223200ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "math" "net" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) func TestAttachTCX(t *testing.T) { testutils.SkipOnOldKernel(t, "6.6", "TCX link") prog := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, "") link, _ := mustAttachTCX(t, prog, ebpf.AttachTCXIngress) testLink(t, link, prog) } func TestTCXAnchor(t *testing.T) { testutils.SkipOnOldKernel(t, "6.6", "TCX link") a := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, "") b := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, "") linkA, iface := mustAttachTCX(t, a, ebpf.AttachTCXEgress) programInfo, err := a.Info() qt.Assert(t, qt.IsNil(err)) programID, _ := programInfo.ID() linkInfo, err := linkA.Info() qt.Assert(t, qt.IsNil(err)) linkID := linkInfo.ID for _, anchor := range []Anchor{ Head(), Tail(), BeforeProgram(a), BeforeProgramByID(programID), AfterLink(linkA), AfterLinkByID(linkID), } { t.Run(fmt.Sprintf("%T", anchor), func(t *testing.T) { linkB, err := AttachTCX(TCXOptions{ Program: b, Attach: ebpf.AttachTCXEgress, Interface: iface, Anchor: anchor, }) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(linkB.Close())) }) } } func TestTCXExpectedRevision(t *testing.T) { testutils.SkipOnOldKernel(t, "6.6", "TCX link") iface, err := net.InterfaceByName("lo") qt.Assert(t, qt.IsNil(err)) _, err = AttachTCX(TCXOptions{ Program: mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, ""), Attach: ebpf.AttachTCXEgress, Interface: iface.Index, ExpectedRevision: math.MaxUint64, }) qt.Assert(t, qt.ErrorIs(err, unix.ESTALE)) } func mustAttachTCX(tb testing.TB, prog *ebpf.Program, attachType ebpf.AttachType) (Link, int) { iface, err := net.InterfaceByName("lo") qt.Assert(tb, qt.IsNil(err)) link, err := AttachTCX(TCXOptions{ Program: prog, Attach: attachType, Interface: iface.Index, }) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { qt.Assert(tb, qt.IsNil(link.Close())) }) return link, iface.Index } golang-github-cilium-ebpf-0.21.0+ds1/link/tracepoint.go000066400000000000000000000036261520243672000226420ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/tracefs" ) // TracepointOptions defines additional parameters that will be used // when loading Tracepoints. type TracepointOptions struct { // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. // // Needs kernel 5.15+. Cookie uint64 } // Tracepoint attaches the given eBPF program to the tracepoint with the given // group and name. See /sys/kernel/tracing/events to find available // tracepoints. The top-level directory is the group, the event's subdirectory // is the name. Example: // // tp, err := Tracepoint("syscalls", "sys_enter_fork", prog, nil) // // Losing the reference to the resulting Link (tp) will close the Tracepoint // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is // only possible as of kernel 4.14 (commit cf5f5ce). // // The returned Link may implement [PerfEvent]. func Tracepoint(group, name string, prog *ebpf.Program, opts *TracepointOptions) (Link, error) { if group == "" || name == "" { return nil, fmt.Errorf("group and name cannot be empty: %w", errInvalidInput) } if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if prog.Type() != ebpf.TracePoint { return nil, fmt.Errorf("eBPF program type %s is not a Tracepoint: %w", prog.Type(), errInvalidInput) } tid, err := tracefs.EventID(group, name) if err != nil { return nil, err } fd, err := openTracepointPerfEvent(tid, perfAllThreads) if err != nil { return nil, err } var cookie uint64 if opts != nil { cookie = opts.Cookie } pe := newPerfEvent(fd, nil) lnk, err := attachPerfEvent(pe, prog, cookie) if err != nil { pe.Close() return nil, err } return lnk, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/tracepoint_test.go000066400000000000000000000063071520243672000237000ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "os" "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" "github.com/go-quicktest/qt" ) func TestTracepoint(t *testing.T) { // Requires at least 4.7 (98b5c2c65c29 "perf, bpf: allow bpf programs attach to tracepoints") testutils.SkipOnOldKernel(t, "4.7", "tracepoint support") prog := mustLoadProgram(t, ebpf.TracePoint, 0, "") // printk is guaranteed to be present. // Kernels before 4.14 don't support attaching to syscall tracepoints. tp, err := Tracepoint("printk", "console", prog, nil) if err != nil { t.Fatal(err) } if err := tp.Close(); err != nil { t.Error("closing tracepoint:", err) } } func TestTracepointMissing(t *testing.T) { // Requires at least 4.7 (98b5c2c65c29 "perf, bpf: allow bpf programs attach to tracepoints") testutils.SkipOnOldKernel(t, "4.7", "tracepoint support") prog := mustLoadProgram(t, ebpf.TracePoint, 0, "") _, err := Tracepoint("missing", "foobazbar", prog, nil) if !errors.Is(err, os.ErrNotExist) { t.Error("Expected os.ErrNotExist, got", err) } } func TestTracepointErrors(t *testing.T) { // Invalid Tracepoint incantations. _, err := Tracepoint("", "", nil, nil) // empty names qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = Tracepoint("_", "_", nil, nil) // empty prog qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = Tracepoint(".", "+", &ebpf.Program{}, nil) // illegal chars in group/name qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = Tracepoint("foo", "bar", &ebpf.Program{}, nil) // wrong prog type qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) } func TestTracepointProgramCall(t *testing.T) { // Kernels before 4.14 don't support attaching to syscall tracepoints. testutils.SkipOnOldKernel(t, "4.14", "syscalls tracepoint support") m, p := newUpdaterMapProg(t, ebpf.TracePoint, 0) // Open Tracepoint at /sys/kernel/tracing/events/syscalls/sys_enter_getpid // and attach it to the ebpf program created above. tp, err := Tracepoint("syscalls", "sys_enter_getpid", p, nil) if err != nil { t.Fatal(err) } // Trigger ebpf program call. unix.Getpid() // Assert that the value got incremented to at least 1, while allowing // for bigger values, because we could race with other getpid callers. assertMapValueGE(t, m, 0, 1) // Detach the Tracepoint. if err := tp.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. unix.Getpid() // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) } func TestTracepointInfo(t *testing.T) { testutils.SkipOnOldKernel(t, "6.6", "bpf_link_info_perf_event") prog := mustLoadProgram(t, ebpf.TracePoint, 0, "") // printk is guaranteed to be present. // Kernels before 4.14 don't support attaching to syscall tracepoints. tp, err := Tracepoint("printk", "console", prog, nil) if err != nil { t.Fatal(err) } defer tp.Close() info, err := tp.Info() if err != nil { t.Fatal(err) } tpInfo := info.PerfEvent().Tracepoint() qt.Assert(t, qt.Equals(tpInfo.Tracepoint, "console")) } golang-github-cilium-ebpf-0.21.0+ds1/link/tracing.go000066400000000000000000000135411520243672000221160ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) type tracing struct { RawLink } func (f *tracing) Update(_ *ebpf.Program) error { return fmt.Errorf("tracing update: %w", ErrNotSupported) } func (f *tracing) Info() (*Info, error) { var info sys.TracingLinkInfo if err := sys.ObjInfo(f.fd, &info); err != nil { return nil, fmt.Errorf("tracing link info: %s", err) } extra := &TracingInfo{ TargetObjectId: info.TargetObjId, TargetBtfId: info.TargetBtfId, AttachType: info.AttachType, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } // AttachFreplace attaches the given eBPF program to the function it replaces. // // The program and name can either be provided at link time, or can be provided // at program load time. If they were provided at load time, they should be nil // and empty respectively here, as they will be ignored by the kernel. // Examples: // // AttachFreplace(dispatcher, "function", replacement) // AttachFreplace(nil, "", replacement) func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (Link, error) { if (name == "") != (targetProg == nil) { return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput) } if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if prog.Type() != ebpf.Extension { return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput) } var ( target int typeID btf.TypeID ) if targetProg != nil { btfHandle, err := targetProg.Handle() if err != nil { return nil, err } defer btfHandle.Close() spec, err := btfHandle.Spec(nil) if err != nil { return nil, err } var function *btf.Func if err := spec.TypeByName(name, &function); err != nil { return nil, err } target = targetProg.FD() typeID, err = spec.TypeID(function) if err != nil { return nil, err } } link, err := AttachRawLink(RawLinkOptions{ Target: target, Program: prog, Attach: ebpf.AttachNone, BTF: typeID, }) if errors.Is(err, sys.ENOTSUPP) { // This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke. return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported) } if err != nil { return nil, err } return &tracing{*link}, nil } type TracingOptions struct { // Program must be of type Tracing with attach type // AttachTraceFEntry/AttachTraceFExit/AttachModifyReturn or // AttachTraceRawTp. Program *ebpf.Program // Program attach type. Can be one of: // - AttachTraceFEntry // - AttachTraceFExit // - AttachModifyReturn // - AttachTraceRawTp // This field is optional. AttachType ebpf.AttachType // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. Cookie uint64 } type LSMOptions struct { // Program must be of type LSM with attach type // AttachLSMMac. Program *ebpf.Program // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. Cookie uint64 } // attachBTFID links all BPF program types (Tracing/LSM) that they attach to a btf_id. func attachBTFID(program *ebpf.Program, at ebpf.AttachType, cookie uint64) (Link, error) { if program.FD() < 0 { return nil, fmt.Errorf("invalid program %w", sys.ErrClosedFd) } var ( fd *sys.FD err error ) switch at { case ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachTraceRawTp, ebpf.AttachModifyReturn, ebpf.AttachLSMMac: // Attach via BPF link fd, err = sys.LinkCreateTracing(&sys.LinkCreateTracingAttr{ ProgFd: uint32(program.FD()), AttachType: sys.AttachType(at), Cookie: cookie, }) if err == nil { break } if !errors.Is(err, unix.EINVAL) && !errors.Is(err, sys.ENOTSUPP) { return nil, fmt.Errorf("create tracing link: %w", err) } fallthrough case ebpf.AttachNone: // Attach via RawTracepointOpen if cookie > 0 { return nil, fmt.Errorf("create raw tracepoint with cookie: %w", ErrNotSupported) } fd, err = sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{ ProgFd: uint32(program.FD()), }) if errors.Is(err, sys.ENOTSUPP) { // This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke. return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported) } if err != nil { return nil, fmt.Errorf("create raw tracepoint: %w", err) } default: return nil, fmt.Errorf("invalid attach type: %s", at.String()) } raw := RawLink{fd: fd} info, err := raw.Info() if err != nil { raw.Close() return nil, err } if info.Type == RawTracepointType { // Sadness upon sadness: a Tracing program with AttachRawTp returns // a raw_tracepoint link. Other types return a tracing link. return &rawTracepoint{raw}, nil } return &tracing{raw}, nil } // AttachTracing links a tracing (fentry/fexit/fmod_ret) BPF program or // a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined // in kernel modules. func AttachTracing(opts TracingOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.Tracing { return nil, fmt.Errorf("invalid program type %s, expected Tracing", t) } switch opts.AttachType { case ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachModifyReturn, ebpf.AttachTraceRawTp, ebpf.AttachNone: default: return nil, fmt.Errorf("invalid attach type: %s", opts.AttachType.String()) } return attachBTFID(opts.Program, opts.AttachType, opts.Cookie) } // AttachLSM links a Linux security module (LSM) BPF Program to a BPF // hook defined in kernel modules. func AttachLSM(opts LSMOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.LSM { return nil, fmt.Errorf("invalid program type %s, expected LSM", t) } return attachBTFID(opts.Program, ebpf.AttachLSMMac, opts.Cookie) } golang-github-cilium-ebpf-0.21.0+ds1/link/tracing_test.go000066400000000000000000000104241520243672000231520ustar00rootroot00000000000000//go:build !windows package link import ( "testing" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) func TestFreplace(t *testing.T) { testutils.SkipOnOldKernel(t, "5.10", "freplace") file := testutils.NativeFile(t, "../testdata/freplace-%s.elf") spec, err := ebpf.LoadCollectionSpec(file) if err != nil { t.Fatal("Can't parse ELF:", err) } target, err := ebpf.NewProgram(spec.Programs["sched_process_exec"]) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create target program:", err) } defer target.Close() // Test attachment specified at load time spec.Programs["replacement"].AttachTarget = target replacement, err := ebpf.NewProgram(spec.Programs["replacement"]) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create replacement program:", err) } defer replacement.Close() freplace, err := AttachFreplace(nil, "", replacement) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create freplace:", err) } testLink(t, freplace, replacement) } func TestFentryFexit(t *testing.T) { testutils.SkipOnOldKernel(t, "5.5", "fentry") spec, err := ebpf.LoadCollectionSpec(testutils.NativeFile(t, "../testdata/fentry_fexit-%s.elf")) if err != nil { t.Fatal("Can't parse ELF:", err) } target, err := ebpf.NewProgram(spec.Programs["target"]) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't create target program:", err) } defer target.Close() for _, name := range []string{"trace_on_entry", "trace_on_exit"} { progSpec := spec.Programs[name] t.Run(name, func(t *testing.T) { progSpec.AttachTarget = target prog, err := ebpf.NewProgram(progSpec) if err != nil { t.Fatal(err) } defer prog.Close() t.Run("link", func(t *testing.T) { testutils.SkipOnOldKernel(t, "5.11", "BPF_LINK_TYPE_TRACING") tracingLink, err := AttachTracing(TracingOptions{ Program: prog, }) if err != nil { t.Fatal("Can't attach tracing:", err) } defer tracingLink.Close() testLink(t, tracingLink, prog) }) }) } } func TestTracing(t *testing.T) { testutils.SkipOnOldKernel(t, "5.11", "BPF_LINK_TYPE_TRACING") tests := []struct { name string attachTo string programType ebpf.ProgramType programAttachType, attachTypeOpt ebpf.AttachType cookie uint64 }{ { name: "AttachTraceFEntry", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFEntry, }, { name: "AttachTraceFEntry", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFEntry, attachTypeOpt: ebpf.AttachTraceFEntry, cookie: 1, }, { name: "AttachTraceFEntry", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFEntry, }, { name: "AttachTraceFExit", attachTo: "inet_dgram_connect", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceFExit, }, { name: "AttachModifyReturn", attachTo: "bpf_modify_return_test", programType: ebpf.Tracing, programAttachType: ebpf.AttachModifyReturn, }, { name: "AttachTraceRawTp", attachTo: "kfree_skb", programType: ebpf.Tracing, programAttachType: ebpf.AttachTraceRawTp, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { prog := mustLoadProgram(t, tt.programType, tt.programAttachType, tt.attachTo) opts := TracingOptions{Program: prog, AttachType: tt.attachTypeOpt, Cookie: tt.cookie} link, err := AttachTracing(opts) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } testLink(t, link, prog) if err = link.Close(); err != nil { t.Fatal(err) } }) } } func TestLSM(t *testing.T) { testutils.SkipOnOldKernel(t, "5.11", "BPF_LINK_TYPE_TRACING") prog := mustLoadProgram(t, ebpf.LSM, ebpf.AttachLSMMac, "file_mprotect") link, err := AttachLSM(LSMOptions{Program: prog}) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } testLink(t, link, prog) } golang-github-cilium-ebpf-0.21.0+ds1/link/uprobe.go000066400000000000000000000252141520243672000217630ustar00rootroot00000000000000//go:build !windows package link import ( "debug/elf" "errors" "fmt" "io/fs" "os" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/tracefs" ) var ( uprobeRefCtrOffsetPMUPath = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset" // elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799 uprobeRefCtrOffsetShift = 32 haveRefCtrOffsetPMU = internal.NewFeatureTest("RefCtrOffsetPMU", func() error { _, err := os.Stat(uprobeRefCtrOffsetPMUPath) if errors.Is(err, os.ErrNotExist) { return internal.ErrNotSupported } if err != nil { return err } return nil }, "4.20") // ErrNoSymbol indicates that the given symbol was not found // in the ELF symbols table. ErrNoSymbol = errors.New("not found") ) const permExec fs.FileMode = 0111 // Executable defines an executable program on the filesystem. type Executable struct { // Path of the executable on the filesystem. path string // Cached symbol values for all ELF and dynamic symbols. // Before using this, lazyLoadSymbols() must be called. cachedSymbols map[string]symbol // cachedSymbolsOnce tracks the lazy load of cachedSymbols. cachedSymbolsOnce sync.Once } type symbol struct { addr uint64 size uint64 } // contains returns true if the given address falls within the range // covered by the symbol. func (s symbol) contains(address uint64) bool { return s.addr <= address && address < s.addr+s.size } // UprobeOptions defines additional parameters that will be used // when loading Uprobes. type UprobeOptions struct { // Symbol address. Must be provided in case of external symbols (shared libs). // If set, overrides the address eventually parsed from the executable. Address uint64 // The offset relative to given symbol. Useful when tracing an arbitrary point // inside the frame of given symbol. // // Note: this field changed from being an absolute offset to being relative // to Address. Offset uint64 // Only set the uprobe on the given process ID. Useful when tracing // shared library calls or programs that have many running instances. PID int // Automatically manage SDT reference counts (semaphores). // // If this field is set, the Kernel will increment/decrement the // semaphore located in the process memory at the provided address on // probe attach/detach. // // See also: // sourceware.org/systemtap/wiki/UserSpaceProbeImplementation (Semaphore Handling) // github.com/torvalds/linux/commit/1cc33161a83d // github.com/torvalds/linux/commit/a6ca88b241d5 RefCtrOffset uint64 // Arbitrary value that can be fetched from an eBPF program // via `bpf_get_attach_cookie()`. // // Needs kernel 5.15+. Cookie uint64 // Prefix used for the event name if the uprobe must be attached using tracefs. // The group name will be formatted as `_`. // The default empty string is equivalent to "ebpf" as the prefix. TraceFSPrefix string } func (uo *UprobeOptions) cookie() uint64 { if uo == nil { return 0 } return uo.Cookie } // To open a new Executable, use: // // OpenExecutable("/bin/bash") // // The returned value can then be used to open Uprobe(s). func OpenExecutable(path string) (*Executable, error) { if path == "" { return nil, fmt.Errorf("path cannot be empty") } info, err := os.Stat(path) if err != nil { return nil, fmt.Errorf("stat executable: %w", err) } if info.Mode()&permExec == 0 { return nil, fmt.Errorf("file %s is not executable", path) } return &Executable{ path: path, cachedSymbols: make(map[string]symbol), }, nil } func (ex *Executable) load(f *internal.SafeELFFile) error { syms, err := f.Symbols() if err != nil && !errors.Is(err, elf.ErrNoSymbols) { return err } dynsyms, err := f.DynamicSymbols() if err != nil && !errors.Is(err, elf.ErrNoSymbols) { return err } syms = append(syms, dynsyms...) for _, s := range syms { if elf.ST_TYPE(s.Info) != elf.STT_FUNC { // Symbol not associated with a function or other executable code. continue } address := s.Value // Loop over ELF segments. for _, prog := range f.Progs { // Skip uninteresting segments. if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 { continue } if prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) { // If the symbol value is contained in the segment, calculate // the symbol offset. // // fn symbol offset = fn symbol VA - .text VA + .text offset // // stackoverflow.com/a/40249502 address = s.Value - prog.Vaddr + prog.Off break } } ex.cachedSymbols[s.Name] = symbol{ addr: address, size: s.Size, } } return nil } func (ex *Executable) lazyLoadSymbols() error { var err error ex.cachedSymbolsOnce.Do(func() { var f *internal.SafeELFFile f, err = internal.OpenSafeELFFile(ex.path) if err != nil { err = fmt.Errorf("parse ELF file: %w", err) return } defer f.Close() if f.Type != elf.ET_EXEC && f.Type != elf.ET_DYN { // ELF is not an executable or a shared object. err = errors.New("the given file is not an executable or a shared object") return } err = ex.load(f) }) return err } // address calculates the address of a symbol in the executable. // // opts must not be nil. func (ex *Executable) address(symbol string, address, offset uint64) (uint64, error) { if address > 0 { return address + offset, nil } err := ex.lazyLoadSymbols() if err != nil { return 0, fmt.Errorf("lazy load symbols: %w", err) } sym, ok := ex.cachedSymbols[symbol] if !ok { return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol) } // Symbols with location 0 from section undef are shared library calls and // are relocated before the binary is executed. Dynamic linking is not // implemented by the library, so mark this as unsupported for now. // // Since only offset values are stored and not elf.Symbol, if the value is 0, // assume it's an external symbol. if sym.addr == 0 { return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+ "(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported) } if offset >= sym.size { return 0, fmt.Errorf("offset %d is out of range of symbol %s", offset, symbol) } return sym.addr + offset, nil } // SymbolOffset represents an offset within a symbol within a binary. type SymbolOffset struct { Symbol string Offset uint64 } // Symbol returns the SymbolOffset that the given address points to. // This includes the symbol name and the offset within that symbol. // // If no symbol is found for the given address, ErrNoSymbol is returned. func (ex *Executable) Symbol(address uint64) (SymbolOffset, error) { if err := ex.lazyLoadSymbols(); err != nil { return SymbolOffset{}, fmt.Errorf("lazy load symbols: %w", err) } for name, symbol := range ex.cachedSymbols { if symbol.contains(address) { return SymbolOffset{name, address - symbol.addr}, nil } } return SymbolOffset{}, ErrNoSymbol } // Uprobe attaches the given eBPF program to a perf event that fires when the // given symbol starts executing in the given Executable. // For example, /bin/bash::main(): // // ex, _ = OpenExecutable("/bin/bash") // ex.Uprobe("main", prog, nil) // // When using symbols which belongs to shared libraries, // an offset must be provided via options: // // up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123}) // // Note: Setting the Offset field in the options supersedes the symbol's offset. // // Losing the reference to the resulting Link (up) will close the Uprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // Functions provided by shared libraries can currently not be traced and // will result in an ErrNotSupported. // // The returned Link may implement [PerfEvent]. func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { u, err := ex.uprobe(symbol, prog, opts, false) if err != nil { return nil, err } lnk, err := attachPerfEvent(u, prog, opts.cookie()) if err != nil { u.Close() return nil, err } return lnk, nil } // Uretprobe attaches the given eBPF program to a perf event that fires right // before the given symbol exits. For example, /bin/bash::main(): // // ex, _ = OpenExecutable("/bin/bash") // ex.Uretprobe("main", prog, nil) // // When using symbols which belongs to shared libraries, // an offset must be provided via options: // // up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123}) // // Note: Setting the Offset field in the options supersedes the symbol's offset. // // Losing the reference to the resulting Link (up) will close the Uprobe // and prevent further execution of prog. The Link must be Closed during // program shutdown to avoid leaking system resources. // // Functions provided by shared libraries can currently not be traced and // will result in an ErrNotSupported. // // The returned Link may implement [PerfEvent]. func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { u, err := ex.uprobe(symbol, prog, opts, true) if err != nil { return nil, err } lnk, err := attachPerfEvent(u, prog, opts.cookie()) if err != nil { u.Close() return nil, err } return lnk, nil } // uprobe opens a perf event for the given binary/symbol and attaches prog to it. // If ret is true, create a uretprobe. func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions, ret bool) (*perfEvent, error) { if prog == nil { return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) } if prog.Type() != ebpf.Kprobe { return nil, fmt.Errorf("eBPF program type %s is not Kprobe: %w", prog.Type(), errInvalidInput) } if opts == nil { opts = &UprobeOptions{} } offset, err := ex.address(symbol, opts.Address, opts.Offset) if err != nil { return nil, err } pid := opts.PID if pid == 0 { pid = perfAllThreads } if opts.RefCtrOffset != 0 { if err := haveRefCtrOffsetPMU(); err != nil { return nil, fmt.Errorf("uprobe ref_ctr_offset: %w", err) } } args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: symbol, Path: ex.path, Offset: offset, Pid: pid, RefCtrOffset: opts.RefCtrOffset, Ret: ret, Cookie: opts.Cookie, Group: opts.TraceFSPrefix, } // Use uprobe PMU if the kernel has it available. tp, err := pmuProbe(args) if err == nil { return tp, nil } if !errors.Is(err, ErrNotSupported) { return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err) } // Use tracefs if uprobe PMU is missing. tp, err = tracefsProbe(args) if err != nil { return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err) } return tp, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/uprobe_multi.go000066400000000000000000000141601520243672000231730ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "fmt" "os" "github.com/cilium/ebpf" "github.com/cilium/ebpf/features" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // UprobeMultiOptions defines additional parameters that will be used // when opening a UprobeMulti Link. type UprobeMultiOptions struct { // Symbol addresses. If set, overrides the addresses eventually parsed from // the executable. Mutually exclusive with UprobeMulti's symbols argument. Addresses []uint64 // Offsets into functions provided by UprobeMulti's symbols argument. // For example: to set uprobes to main+5 and _start+10, call UprobeMulti // with: // symbols: "main", "_start" // opt.Offsets: 5, 10 Offsets []uint64 // Optional list of associated ref counter offsets. RefCtrOffsets []uint64 // Optional list of associated BPF cookies. Cookies []uint64 // Only set the uprobe_multi link on the given process ID, zero PID means // system-wide. PID uint32 } func (ex *Executable) UprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) { return ex.uprobeMulti(symbols, prog, opts, 0) } func (ex *Executable) UretprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) { // The return probe is not limited for symbols entry, so there's no special // setup for return uprobes (other than the extra flag). The symbols, opts.Offsets // and opts.Addresses arrays follow the same logic as for entry uprobes. return ex.uprobeMulti(symbols, prog, opts, sys.BPF_F_UPROBE_MULTI_RETURN) } func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions, flags uint32) (Link, error) { if prog == nil { return nil, errors.New("cannot attach a nil program") } if opts == nil { opts = &UprobeMultiOptions{} } addresses, err := ex.addresses(symbols, opts.Addresses, opts.Offsets) if err != nil { return nil, err } addrs := len(addresses) cookies := len(opts.Cookies) refCtrOffsets := len(opts.RefCtrOffsets) if addrs == 0 { return nil, fmt.Errorf("field Addresses is required: %w", errInvalidInput) } if refCtrOffsets > 0 && refCtrOffsets != addrs { return nil, fmt.Errorf("field RefCtrOffsets must be exactly Addresses in length: %w", errInvalidInput) } if cookies > 0 && cookies != addrs { return nil, fmt.Errorf("field Cookies must be exactly Addresses in length: %w", errInvalidInput) } attr := &sys.LinkCreateUprobeMultiAttr{ Path: sys.NewStringPointer(ex.path), ProgFd: uint32(prog.FD()), AttachType: sys.BPF_TRACE_UPROBE_MULTI, UprobeMultiFlags: flags, Count: uint32(addrs), Offsets: sys.SlicePointer(addresses), Pid: opts.PID, } if refCtrOffsets != 0 { attr.RefCtrOffsets = sys.SlicePointer(opts.RefCtrOffsets) } if cookies != 0 { attr.Cookies = sys.SlicePointer(opts.Cookies) } fd, err := sys.LinkCreateUprobeMulti(attr) if errors.Is(err, unix.ESRCH) { return nil, fmt.Errorf("%w (specified pid not found?)", os.ErrNotExist) } // Since Linux commit 46ba0e49b642 ("bpf: fix multi-uprobe PID filtering // logic"), if the provided pid overflows MaxInt32 (turning it negative), the // kernel will return EINVAL instead of ESRCH. if errors.Is(err, unix.EINVAL) { return nil, fmt.Errorf("%w (invalid pid, missing symbol or prog's AttachType not AttachTraceUprobeMulti?)", err) } if err != nil { if haveFeatErr := features.HaveBPFLinkUprobeMulti(); haveFeatErr != nil { return nil, haveFeatErr } return nil, err } return &uprobeMultiLink{RawLink{fd, ""}}, nil } func (ex *Executable) addresses(symbols []string, addresses, offsets []uint64) ([]uint64, error) { n := len(symbols) if n == 0 { n = len(addresses) } if n == 0 { return nil, fmt.Errorf("%w: neither symbols nor addresses given", errInvalidInput) } if symbols != nil && len(symbols) != n { return nil, fmt.Errorf("%w: have %d symbols but want %d", errInvalidInput, len(symbols), n) } if addresses != nil && len(addresses) != n { return nil, fmt.Errorf("%w: have %d addresses but want %d", errInvalidInput, len(addresses), n) } if offsets != nil && len(offsets) != n { return nil, fmt.Errorf("%w: have %d offsets but want %d", errInvalidInput, len(offsets), n) } results := make([]uint64, 0, n) for i := 0; i < n; i++ { var sym string if symbols != nil { sym = symbols[i] } var addr, off uint64 if addresses != nil { addr = addresses[i] } if offsets != nil { off = offsets[i] } result, err := ex.address(sym, addr, off) if err != nil { return nil, err } results = append(results, result) } return results, nil } type uprobeMultiLink struct { RawLink } var _ Link = (*uprobeMultiLink)(nil) func (kml *uprobeMultiLink) Update(_ *ebpf.Program) error { return fmt.Errorf("update uprobe_multi: %w", ErrNotSupported) } func (kml *uprobeMultiLink) Info() (*Info, error) { var info sys.UprobeMultiLinkInfo if err := sys.ObjInfo(kml.fd, &info); err != nil { return nil, fmt.Errorf("uprobe multi link info: %s", err) } var ( path = make([]byte, info.PathSize) refCtrOffsets = make([]uint64, info.Count) addrs = make([]uint64, info.Count) cookies = make([]uint64, info.Count) ) info = sys.UprobeMultiLinkInfo{ Path: sys.SlicePointer(path), PathSize: uint32(len(path)), Offsets: sys.SlicePointer(addrs), RefCtrOffsets: sys.SlicePointer(refCtrOffsets), Cookies: sys.SlicePointer(cookies), Count: uint32(len(addrs)), } if err := sys.ObjInfo(kml.fd, &info); err != nil { return nil, fmt.Errorf("uprobe multi link info: %s", err) } if info.Path.IsNil() { path = nil } if info.Cookies.IsNil() { cookies = nil } if info.Offsets.IsNil() { addrs = nil } if info.RefCtrOffsets.IsNil() { refCtrOffsets = nil } extra := &UprobeMultiInfo{ Count: info.Count, Flags: info.Flags, pid: info.Pid, offsets: addrs, cookies: cookies, refCtrOffsets: refCtrOffsets, File: unix.ByteSliceToString(path), } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/uprobe_multi_test.go000066400000000000000000000170501520243672000242330ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "math" "os" "os/exec" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/features" "github.com/cilium/ebpf/internal/testutils" ) func TestUprobeMulti(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") // uprobe um, err := bashEx.UprobeMulti(bashSyms, prog, nil) if err != nil { t.Fatal(err) } testLink(t, um, prog) _ = um.Close() // uretprobe um, err = bashEx.UretprobeMulti(bashSyms, prog, nil) if err != nil { t.Fatal(err) } testLink(t, um, prog) _ = um.Close() } func TestUprobeMultiInfo(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti()) testutils.SkipOnOldKernel(t, "6.8", "bpf_link_info_uprobe_multi") prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") // uprobe um, err := bashEx.UprobeMulti(bashSyms, prog, nil) if err != nil { t.Fatal(err) } defer um.Close() linkInfo, err := um.Info() if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(linkInfo.Type, UprobeMultiType)) uprobeDetails := linkInfo.UprobeMulti() // On some platforms, /bin/bash may point to /usr/bin/bash, thus only a contains and no equals check qt.Assert(t, qt.StringContains(uprobeDetails.File, bashEx.path)) uprobeOffsets, ok := uprobeDetails.Offsets() qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.HasLen(uprobeOffsets, len(bashSyms))) bashElf, err := OpenExecutable(uprobeDetails.File) qt.Assert(t, qt.IsNil(err)) var symnames = make([]string, len(uprobeOffsets)) for i, offset := range uprobeOffsets { symOffset, err := bashElf.Symbol(offset.Offset) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(symOffset.Offset, 0)) symnames[i] = symOffset.Symbol } qt.Assert(t, qt.ContentEquals(symnames, bashSyms)) } func TestUprobeMultiInput(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") // Always doing same test for both uprobe and uretprobe // One of symbols or offsets must be given. _, err := bashEx.UprobeMulti([]string{}, prog, nil) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = bashEx.UretprobeMulti([]string{}, prog, nil) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) // One address, two cookies. _, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{ Addresses: []uint64{1}, Cookies: []uint64{2, 3}, }) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{ Addresses: []uint64{1}, Cookies: []uint64{2, 3}, }) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) // Two addresses, one refctr offset. _, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{ Addresses: []uint64{1, 2}, RefCtrOffsets: []uint64{4}, }) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{ Addresses: []uint64{1, 2}, RefCtrOffsets: []uint64{4}, }) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) // It's either symbols or addresses. _, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{ Addresses: []uint64{1}, }) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{ Addresses: []uint64{1}, }) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) // No addresses and no symbols _, err = bashEx.UprobeMulti([]string{}, prog, nil) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) _, err = bashEx.UretprobeMulti([]string{}, prog, nil) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) // PID not found _, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{ // pid_t is int32, overflowing it will return EINVAL. PID: math.MaxInt32, }) qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) _, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{ PID: math.MaxInt32, }) qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) } func TestUprobeMultiResolveOk(t *testing.T) { addrSym1, err := bashEx.address(bashSyms[0], 0, 0) qt.Assert(t, qt.IsNil(err)) addrSym2, err := bashEx.address(bashSyms[1], 0, 0) qt.Assert(t, qt.IsNil(err)) addrSym3, err := bashEx.address(bashSyms[2], 0, 0) qt.Assert(t, qt.IsNil(err)) addrs, err := bashEx.addresses(bashSyms, nil, nil) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1, addrSym2, addrSym3})) addrs, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10, 11}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1 + 5, addrSym2 + 10, addrSym3 + 11})) addrs, err = bashEx.addresses(bashSyms, []uint64{1, 2, 3}, nil) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(addrs, []uint64{1, 2, 3})) } func TestUprobeMultiResolveFail(t *testing.T) { // No input _, err := bashEx.addresses(nil, nil, nil) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) // Different dimensions for Addresses and Offsets _, err = bashEx.addresses(nil, []uint64{100, 200}, []uint64{5, 10, 11}) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) // Different dimensions for symbols and Offsets _, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10}) qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) } func TestUprobeMultiCookie(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti()) prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") // uprobe um, err := bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{ Cookies: []uint64{1, 2, 3}, }) if err != nil { t.Fatal(err) } _ = um.Close() // uretprobe um, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{ Cookies: []uint64{3, 2, 1}, }) if err != nil { t.Fatal(err) } _ = um.Close() } func TestUprobeMultiProgramCall(t *testing.T) { testutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti()) // We execute 'bash --help' args := []string{"--help"} elf := "/bin/bash" test := func(retprobe bool, expected uint32) { m, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti) var err error // Load the executable. ex, err := OpenExecutable(elf) if err != nil { t.Fatal(err) } var um Link // Open UprobeMulti on the executable for the given symbol // and attach it to the ebpf program created above. if retprobe { um, err = ex.UretprobeMulti(bashSyms, p, nil) } else { um, err = ex.UprobeMulti(bashSyms, p, nil) } if errors.Is(err, ErrNoSymbol) { // Assume bash_Syms symbols always exist and skip the test // if the symbol can't be found as certain OS (eg. Debian) // strip binaries. t.Skipf("executable %s appear to be stripped, skipping", elf) } if err != nil { t.Fatal(err) } // Trigger ebpf program call. trigger := func(t *testing.T) { if err := exec.Command(elf, args...).Run(); err != nil { t.Fatal(err) } } trigger(t) // Detach link. if err := um.Close(); err != nil { t.Fatal(err) } assertMapValueGE(t, m, 0, expected) // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. trigger(t) // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) } // all 3 uprobes should trigger for entry uprobes test(false, 3) // We have return uprobe installed on main, _start and check_dev_tty // functions, but only check_dev_tty is triggered, because 'bash --help' // calls exit(0). test(true, 1) } golang-github-cilium-ebpf-0.21.0+ds1/link/uprobe_test.go000066400000000000000000000265241520243672000230270ustar00rootroot00000000000000//go:build !windows package link import ( "errors" "go/build" "os" "os/exec" "path" "path/filepath" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) var ( bashEx, _ = OpenExecutable("/bin/bash") bashSyms = []string{"main", "_start", "check_dev_tty"} bashSym = bashSyms[0] ) func TestExecutable(t *testing.T) { _, err := OpenExecutable("") if err == nil { t.Fatal("create executable: expected error on empty path") } _, err = OpenExecutable("/non/existent/path") if err == nil { t.Fatal("create executable: expected error on non-existent path") } var pe *os.PathError qt.Assert(t, qt.ErrorAs(err, &pe)) // create temp non-executable file dir := t.TempDir() path := filepath.Join(dir, "file.txt") err = os.WriteFile(path, []byte("hello"), 0600) if err != nil { t.Fatalf("write file: %v", err) } _, err = OpenExecutable(path) if err == nil { t.Fatal("create executable: expected error on non-executable file") } // make it executable err = os.Chmod(path, 0700) if err != nil { t.Fatalf("chmod file: %v", err) } _, err = OpenExecutable(path) if err != nil { t.Fatalf("create executable: %v", err) } if bashEx.path != "/bin/bash" { t.Fatalf("create executable: unexpected path '%s'", bashEx.path) } _, err = bashEx.address(bashSym, 0, 0) if err != nil { t.Fatalf("find offset: %v", err) } _, err = bashEx.address("bogus", 0, 0) if err == nil { t.Fatal("find symbol: expected error") } } func TestExecutableOffset(t *testing.T) { symbolOffset, err := bashEx.address(bashSym, 0, 0) if err != nil { t.Fatal(err) } offset, err := bashEx.address(bashSym, 0x1, 0) if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(offset, 0x1)) offset, err = bashEx.address(bashSym, 0, 0x2) if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(offset, symbolOffset+0x2)) offset, err = bashEx.address(bashSym, 0x1, 0x2) if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(offset, 0x1+0x2)) } func TestExecutableLazyLoadSymbols(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") ex, err := OpenExecutable("/bin/bash") qt.Assert(t, qt.IsNil(err)) // Addresses must be empty, will be lazy loaded. qt.Assert(t, qt.HasLen(ex.cachedSymbols, 0)) prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // Address must be a multiple of 4 on arm64, see // https://elixir.bootlin.com/linux/v6.6.4/source/arch/arm64/kernel/probes/uprobes.c#L42 up, err := ex.Uprobe(bashSym, prog, &UprobeOptions{Address: 124}) qt.Assert(t, qt.IsNil(err)) up.Close() // Addresses must still be empty as Address has been provided via options. qt.Assert(t, qt.HasLen(ex.cachedSymbols, 0)) up, err = ex.Uprobe(bashSym, prog, nil) qt.Assert(t, qt.IsNil(err)) up.Close() // Symbol table should be loaded. qt.Assert(t, qt.Not(qt.HasLen(ex.cachedSymbols, 0))) } func TestUprobe(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := bashEx.Uprobe(bashSym, prog, nil) qt.Assert(t, qt.IsNil(err)) defer up.Close() testLink(t, up, prog) } func TestUprobeInfo(t *testing.T) { testutils.SkipOnOldKernel(t, "6.6", "bpf_link_info_perf_event") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := bashEx.Uprobe(bashSym, prog, nil) qt.Assert(t, qt.IsNil(err)) defer up.Close() info, err := up.Info() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(info.Type, PerfEventType)) eventInfo := info.PerfEvent() qt.Assert(t, qt.Equals(eventInfo.Type, PerfEventUprobe)) uprobeInfo := eventInfo.Uprobe() qt.Assert(t, qt.StringContains(uprobeInfo.File, bashEx.path)) executable, err := OpenExecutable(uprobeInfo.File) qt.Assert(t, qt.IsNil(err)) sym, err := executable.Symbol(uint64(uprobeInfo.Offset)) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(sym.Symbol, bashSym)) qt.Assert(t, qt.Equals(sym.Offset, 0)) } func TestUprobeExtNotFound(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // This symbol will not be present in Executable (elf.SHN_UNDEF). _, err := bashEx.Uprobe("open", prog, nil) if err == nil { t.Fatal("expected error") } } func TestUprobeExtWithOpts(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // NB: It's not possible to invoke the uprobe since we use an arbitrary // address. up, err := bashEx.Uprobe("open", prog, &UprobeOptions{ // arm64 requires the addresses to be aligned (a multiple of 4) Address: 0x4, }) if err != nil { t.Fatal(err) } defer up.Close() } func TestUprobeWithPID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := bashEx.Uprobe(bashSym, prog, &UprobeOptions{PID: os.Getpid()}) if err != nil { t.Fatal(err) } defer up.Close() } func TestUprobeWithNonExistentPID(t *testing.T) { prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") // trying to open a perf event on a non-existent PID will return ESRCH. _, err := bashEx.Uprobe(bashSym, prog, &UprobeOptions{PID: -2}) if !errors.Is(err, unix.ESRCH) { t.Fatalf("expected ESRCH, got %v", err) } } func TestUretprobe(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") prog := mustLoadProgram(t, ebpf.Kprobe, 0, "") up, err := bashEx.Uretprobe(bashSym, prog, nil) qt.Assert(t, qt.IsNil(err)) defer up.Close() testLink(t, up, prog) } // Test u(ret)probe creation using perf_uprobe PMU. func TestUprobeCreatePMU(t *testing.T) { // Requires at least 4.17 (e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU") testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU") // Fetch the offset from the /bin/bash Executable already defined. off, err := bashEx.address(bashSym, 0, 0) qt.Assert(t, qt.IsNil(err)) // Prepare probe args. args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: bashSym, Path: bashEx.path, Offset: off, Pid: perfAllThreads, } // uprobe PMU pu, err := pmuProbe(args) qt.Assert(t, qt.IsNil(err)) defer pu.Close() // uretprobe PMU args.Ret = true pr, err := pmuProbe(args) qt.Assert(t, qt.IsNil(err)) defer pr.Close() } // Test fallback behaviour on kernels without perf_uprobe PMU available. func TestUprobePMUUnavailable(t *testing.T) { // Fetch the offset from the /bin/bash Executable already defined. off, err := bashEx.address(bashSym, 0, 0) qt.Assert(t, qt.IsNil(err)) // Prepare probe args. args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: bashSym, Path: bashEx.path, Offset: off, Pid: perfAllThreads, } pk, err := pmuProbe(args) if err == nil { pk.Close() t.Skipf("Kernel supports perf_uprobe PMU, not asserting error.") } // Expect ErrNotSupported. qt.Assert(t, qt.ErrorIs(err, ErrNotSupported), qt.Commentf("got error: %s", err)) } // Test tracefs u(ret)probe creation on all kernel versions. func TestUprobeTraceFS(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") // Fetch the offset from the /bin/bash Executable already defined. off, err := bashEx.address(bashSym, 0, 0) qt.Assert(t, qt.IsNil(err)) // Prepare probe args. args := tracefs.ProbeArgs{ Type: tracefs.Uprobe, Symbol: bashSym, Path: bashEx.path, Offset: off, Pid: perfAllThreads, } // Open and close tracefs u(ret)probes, checking all errors. up, err := tracefsProbe(args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(up.Close())) args.Ret = true up, err = tracefsProbe(args) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(up.Close())) // Create two identical trace events, ensure their IDs differ. args.Ret = false u1, err := tracefsProbe(args) qt.Assert(t, qt.IsNil(err)) defer u1.Close() qt.Assert(t, qt.IsNotNil(u1.tracefsEvent)) u2, err := tracefsProbe(args) qt.Assert(t, qt.IsNil(err)) defer u2.Close() qt.Assert(t, qt.IsNotNil(u2.tracefsEvent)) // Compare the uprobes' tracefs IDs. qt.Assert(t, qt.Not(qt.Equals(u1.tracefsEvent.ID(), u2.tracefsEvent.ID()))) // Expect an error when supplying an invalid custom group name args.Group = "/" _, err = tracefsProbe(args) qt.Assert(t, qt.Not(qt.IsNil(err))) args.Group = "customgroup" u3, err := tracefsProbe(args) qt.Assert(t, qt.IsNil(err)) defer u3.Close() qt.Assert(t, qt.Matches(u3.tracefsEvent.Group(), `customgroup_[a-f0-9]{16}`)) } func TestUprobeProgramCall(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") tests := []struct { name string elf string args []string sym string }{ { "bash", "/bin/bash", []string{"--help"}, "main", }, { "go-binary", path.Join(build.Default.GOROOT, "bin/go"), []string{"version"}, "main.main", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.name == "go-binary" { // https://github.com/cilium/ebpf/issues/406 testutils.SkipOnOldKernel(t, "4.14", "uprobes on Go binaries silently fail on kernel < 4.14") } m, p := newUpdaterMapProg(t, ebpf.Kprobe, 0) // Load the executable. ex, err := OpenExecutable(tt.elf) if err != nil { t.Fatal(err) } // Open Uprobe on the executable for the given symbol // and attach it to the ebpf program created above. u, err := ex.Uprobe(tt.sym, p, nil) if errors.Is(err, ErrNoSymbol) { // Assume bash::main and go::main.main always exists // and skip the test if the symbol can't be found as // certain OS (eg. Debian) strip binaries. t.Skipf("executable %s appear to be stripped, skipping", tt.elf) } if err != nil { t.Fatal(err) } // Trigger ebpf program call. trigger := func(t *testing.T) { if err := exec.Command(tt.elf, tt.args...).Run(); err != nil { t.Fatal(err) } } trigger(t) // Assert that the value got incremented to at least 1, while allowing // for bigger values, because we could race with other bash execution. assertMapValueGE(t, m, 0, 1) // Detach the Uprobe. if err := u.Close(); err != nil { t.Fatal(err) } // Reset map value to 0 at index 0. if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { t.Fatal(err) } // Retrigger the ebpf program call. trigger(t) // Assert that this time the value has not been updated. assertMapValue(t, m, 0, 0) }) } } func TestUprobeProgramWrongPID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.14", "uprobe on v4.9 returns EIO on vimto") m, p := newUpdaterMapProg(t, ebpf.Kprobe, 0) // Load the '/bin/bash' executable. ex, err := OpenExecutable("/bin/bash") if err != nil { t.Fatal(err) } // Open Uprobe on '/bin/bash' for the symbol 'main' // and attach it to the ebpf program created above. // Create the perf-event with the current process' PID // to make sure the event is not fired when we will try // to trigger the program execution via exec. u, err := ex.Uprobe("main", p, &UprobeOptions{PID: os.Getpid()}) if err != nil { t.Fatal(err) } defer u.Close() // Trigger ebpf program call. if err := exec.Command("/bin/bash", "--help").Run(); err != nil { t.Fatal(err) } // Assert that the value at index 0 is still 0. assertMapValue(t, m, 0, 0) } func TestHaveRefCtrOffsetPMU(t *testing.T) { testutils.CheckFeatureTest(t, haveRefCtrOffsetPMU) } golang-github-cilium-ebpf-0.21.0+ds1/link/xdp.go000066400000000000000000000034671520243672000212700ustar00rootroot00000000000000//go:build !windows package link import ( "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" ) // XDPAttachFlags represents how XDP program will be attached to interface. type XDPAttachFlags uint32 const ( // XDPGenericMode (SKB) links XDP BPF program for drivers which do // not yet support native XDP. XDPGenericMode XDPAttachFlags = 1 << (iota + 1) // XDPDriverMode links XDP BPF program into the driver’s receive path. XDPDriverMode // XDPOffloadMode offloads the entire XDP BPF program into hardware. XDPOffloadMode ) type XDPOptions struct { // Program must be an XDP BPF program. Program *ebpf.Program // Interface is the interface index to attach program to. Interface int // Flags is one of XDPAttachFlags (optional). // // Only one XDP mode should be set, without flag defaults // to driver/generic mode (best effort). Flags XDPAttachFlags } // AttachXDP links an XDP BPF program to an XDP hook. func AttachXDP(opts XDPOptions) (Link, error) { if t := opts.Program.Type(); t != ebpf.XDP { return nil, fmt.Errorf("invalid program type %s, expected XDP", t) } if opts.Interface < 1 { return nil, fmt.Errorf("invalid interface index: %d", opts.Interface) } rawLink, err := AttachRawLink(RawLinkOptions{ Program: opts.Program, Attach: ebpf.AttachXDP, Target: opts.Interface, Flags: uint32(opts.Flags), }) if err != nil { return nil, fmt.Errorf("failed to attach link: %w", err) } return &xdpLink{*rawLink}, nil } type xdpLink struct { RawLink } func (xdp *xdpLink) Info() (*Info, error) { var info sys.XDPLinkInfo if err := sys.ObjInfo(xdp.fd, &info); err != nil { return nil, fmt.Errorf("xdp link info: %s", err) } extra := &XDPInfo{ Ifindex: info.Ifindex, } return &Info{ info.Type, info.Id, ebpf.ProgramID(info.ProgId), extra, }, nil } golang-github-cilium-ebpf-0.21.0+ds1/link/xdp_test.go000066400000000000000000000012601520243672000223140ustar00rootroot00000000000000//go:build !windows package link import ( "math" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/testutils" ) const IfIndexLO = 1 func TestAttachXDP(t *testing.T) { testutils.SkipOnOldKernel(t, "5.9", "BPF_LINK_TYPE_XDP") prog := mustLoadProgram(t, ebpf.XDP, 0, "") _, err := AttachXDP(XDPOptions{ Program: prog, Interface: math.MaxInt, }) qt.Assert(t, qt.IsNotNil(err)) l, err := AttachXDP(XDPOptions{ Program: prog, Interface: IfIndexLO, }) qt.Assert(t, qt.IsNil(err)) info, err := l.Info() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(info.XDP().Ifindex, IfIndexLO)) testLink(t, l, prog) } golang-github-cilium-ebpf-0.21.0+ds1/linker.go000066400000000000000000000323441520243672000210200ustar00rootroot00000000000000package ebpf import ( "debug/elf" "encoding/binary" "errors" "fmt" "io" "io/fs" "math" "slices" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/kallsyms" "github.com/cilium/ebpf/internal/platform" ) // handles stores handle objects to avoid gc cleanup type handles []*btf.Handle func (hs *handles) add(h *btf.Handle) (int, error) { if h == nil { return 0, nil } if len(*hs) == math.MaxInt16 { return 0, fmt.Errorf("can't add more than %d module FDs to fdArray", math.MaxInt16) } *hs = append(*hs, h) // return length of slice so that indexes start at 1 return len(*hs), nil } func (hs handles) fdArray() []int32 { // first element of fda is reserved as no module can be indexed with 0 fda := []int32{0} for _, h := range hs { fda = append(fda, int32(h.FD())) } return fda } func (hs *handles) Close() error { var errs []error for _, h := range *hs { errs = append(errs, h.Close()) } return errors.Join(errs...) } // The linker is responsible for resolving bpf-to-bpf calls between programs // within an ELF. Each BPF program must be a self-contained binary blob, // so when an instruction in one ELF program section wants to jump to // a function in another, the linker needs to pull in the bytecode // (and BTF info) of the target function and concatenate the instruction // streams. // // Later on in the pipeline, all call sites are fixed up with relative jumps // within this newly-created instruction stream to then finally hand off to // the kernel with BPF_PROG_LOAD. // // Each function is denoted by an ELF symbol and the compiler takes care of // register setup before each jump instruction. // hasFunctionReferences returns true if insns contains one or more bpf2bpf // function references. func hasFunctionReferences(insns asm.Instructions) bool { for _, i := range insns { if i.IsFunctionReference() { return true } } return false } // applyRelocations collects and applies any CO-RE relocations in insns. // // insns are modified in place. func applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builder, c *btf.Cache, kernelOverride *btf.Spec, extraTargets []*btf.Spec) error { var relos []*btf.CORERelocation var reloInsns []*asm.Instruction iter := insns.Iterate() for iter.Next() { if relo := btf.CORERelocationMetadata(iter.Ins); relo != nil { relos = append(relos, relo) reloInsns = append(reloInsns, iter.Ins) } } if len(relos) == 0 { return nil } if bo == nil { bo = internal.NativeEndian } var targets []*btf.Spec if kernelOverride == nil { kernel, err := c.Kernel() if err != nil { return fmt.Errorf("load kernel spec: %w", err) } modules, err := c.Modules() // Ignore ErrNotExists to cater to kernels which have CONFIG_DEBUG_INFO_BTF_MODULES // or CONFIG_DEBUG_INFO_BTF disabled. if err != nil && !errors.Is(err, fs.ErrNotExist) { return err } targets = make([]*btf.Spec, 0, 1+len(modules)+len(extraTargets)) targets = append(targets, kernel) for _, kmod := range modules { spec, err := c.Module(kmod) if err != nil { return fmt.Errorf("load BTF for kmod %s: %w", kmod, err) } targets = append(targets, spec) } } else { // We expect kernelOverride to contain the merged types // of vmlinux and kernel modules, as distributed by btfhub. targets = []*btf.Spec{kernelOverride} } targets = append(targets, extraTargets...) fixups, err := btf.CORERelocate(relos, targets, bo, b.Add) if err != nil { return err } for i, fixup := range fixups { if err := fixup.Apply(reloInsns[i]); err != nil { return fmt.Errorf("fixup for %s: %w", relos[i], err) } } return nil } // flattenPrograms resolves bpf-to-bpf calls for a set of programs. // // Links all programs in names by modifying their ProgramSpec in progs. func flattenPrograms(progs map[string]*ProgramSpec, names []string) { // Pre-calculate all function references. refs := make(map[*ProgramSpec][]string) for _, prog := range progs { refs[prog] = prog.Instructions.FunctionReferences() } // Create a flattened instruction stream, but don't modify progs yet to // avoid linking multiple times. flattened := make([]asm.Instructions, 0, len(names)) for _, name := range names { flattened = append(flattened, flattenInstructions(name, progs, refs)) } // Finally, assign the flattened instructions. for i, name := range names { progs[name].Instructions = flattened[i] } } // flattenInstructions resolves bpf-to-bpf calls for a single program. // // Flattens the instructions of prog by concatenating the instructions of all // direct and indirect dependencies. // // progs contains all referenceable programs, while refs contain the direct // dependencies of each program. func flattenInstructions(name string, progs map[string]*ProgramSpec, refs map[*ProgramSpec][]string) asm.Instructions { prog := progs[name] progRefs := refs[prog] if len(progRefs) == 0 { // No references, nothing to do. return prog.Instructions } insns := make(asm.Instructions, len(prog.Instructions)) copy(insns, prog.Instructions) // Add all direct references of prog to the list of to be linked programs. pending := make([]string, len(progRefs)) copy(pending, progRefs) // All references for which we've appended instructions. linked := make(map[string]bool) // Iterate all pending references. We can't use a range since pending is // modified in the body below. for len(pending) > 0 { var ref string ref, pending = pending[0], pending[1:] if linked[ref] { // We've already linked this ref, don't append instructions again. continue } progRef := progs[ref] if progRef == nil { // We don't have instructions that go with this reference. This // happens when calling extern functions. continue } insns = append(insns, progRef.Instructions...) linked[ref] = true // Make sure we link indirect references. pending = append(pending, refs[progRef]...) } return insns } // fixupAndValidate is called by the ELF reader right before marshaling the // instruction stream. It performs last-minute adjustments to the program and // runs some sanity checks before sending it off to the kernel. func fixupAndValidate(insns asm.Instructions) error { iter := insns.Iterate() for iter.Next() { ins := iter.Ins // Map load was tagged with a Reference, but does not contain a Map pointer. needsMap := ins.Reference() != "" || ins.Metadata.Get(kconfigMetaKey{}) != nil if ins.IsLoadFromMap() && needsMap && ins.Map() == nil { return fmt.Errorf("instruction %d: %w", iter.Index, asm.ErrUnsatisfiedMapReference) } fixupProbeReadKernel(ins) } return nil } // A constant used to poison calls to non-existent kfuncs. // // Similar POISON_CALL_KFUNC_BASE in libbpf, except that we use a value lower // than 2^28 to fit into a tagged constant. const kfuncCallPoisonBase = 0xdedc0de // fixupKfuncs loops over all instructions in search for kfunc calls. // If at least one is found, the current kernels BTF and module BTFis are searched to set Instruction.Constant // and Instruction.Offset to the correct values. func fixupKfuncs(insns asm.Instructions, cache *btf.Cache) (_ handles, err error) { closeOnError := func(c io.Closer) { if err != nil { c.Close() } } iter := insns.Iterate() for iter.Next() { ins := iter.Ins if metadata := ins.Metadata.Get(kfuncMetaKey{}); metadata != nil { goto fixups } } return nil, nil fixups: // Only load kernel BTF if we found at least one kfunc call. kernelSpec can be // nil if the kernel does not have BTF, in which case we poison all kfunc // calls. _, err = cache.Kernel() // ErrNotSupportedOnOS wraps ErrNotSupported, check for it first. if errors.Is(err, internal.ErrNotSupportedOnOS) { return nil, fmt.Errorf("kfuncs are not supported on this platform: %w", err) } if err != nil && !errors.Is(err, ErrNotSupported) { return nil, err } fdArray := make(handles, 0) defer closeOnError(&fdArray) for { ins := iter.Ins metadata := ins.Metadata.Get(kfuncMetaKey{}) if metadata == nil { if !iter.Next() { // break loop if this was the last instruction in the stream. break } continue } // check meta, if no meta return err kfm, _ := metadata.(*kfuncMeta) if kfm == nil { return nil, fmt.Errorf("kfuncMetaKey doesn't contain kfuncMeta") } // findTargetInKernel returns btf.ErrNotFound if the input btf.Spec is nil. target := btf.Type((*btf.Func)(nil)) spec, module, err := findTargetInKernel(kfm.Func.Name, &target, cache) if errors.Is(err, btf.ErrNotFound) { if kfm.Binding == elf.STB_WEAK { if ins.IsKfuncCall() { // If the kfunc call is weak and not found, poison the call. Use a // recognizable constant to make it easier to debug. fn, err := asm.BuiltinFuncForPlatform(platform.Native, kfuncCallPoisonBase) if err != nil { return nil, err } *ins = fn.Call() } else if ins.OpCode.IsDWordLoad() { // If the kfunc DWordLoad is weak and not found, set its address to 0. ins.Constant = 0 ins.Src = 0 } else { return nil, fmt.Errorf("only kfunc calls and dword loads may have kfunc metadata") } iter.Next() continue } // Error on non-weak kfunc not found. return nil, fmt.Errorf("kfunc %q: %w", kfm.Func.Name, ErrNotSupported) } if err != nil { return nil, fmt.Errorf("finding kfunc in kernel: %w", err) } idx, err := fdArray.add(module) if err != nil { return nil, err } if err := btf.CheckTypeCompatibility(kfm.Func.Type, target.(*btf.Func).Type); err != nil { return nil, &incompatibleKfuncError{kfm.Func.Name, err} } id, err := spec.TypeID(target) if err != nil { return nil, err } ins.Constant = int64(id) ins.Offset = int16(idx) if !iter.Next() { break } } return fdArray, nil } type incompatibleKfuncError struct { name string err error } func (ike *incompatibleKfuncError) Error() string { return fmt.Sprintf("kfunc %q: %s", ike.name, ike.err) } // fixupProbeReadKernel replaces calls to bpf_probe_read_{kernel,user}(_str) // with bpf_probe_read(_str) on kernels that don't support it yet. func fixupProbeReadKernel(ins *asm.Instruction) { if !ins.IsBuiltinCall() { return } // Kernel supports bpf_probe_read_kernel, nothing to do. if haveProbeReadKernel() == nil { return } switch asm.BuiltinFunc(ins.Constant) { case asm.FnProbeReadKernel, asm.FnProbeReadUser: ins.Constant = int64(asm.FnProbeRead) case asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr: ins.Constant = int64(asm.FnProbeReadStr) } } // resolveKconfigReferences creates and populates a .kconfig map if necessary. // // Returns a nil Map and no error if no references exist. func resolveKconfigReferences(insns asm.Instructions) (_ *Map, err error) { closeOnError := func(c io.Closer) { if err != nil { c.Close() } } var spec *MapSpec iter := insns.Iterate() for iter.Next() { meta, _ := iter.Ins.Metadata.Get(kconfigMetaKey{}).(*kconfigMeta) if meta != nil { spec = meta.Map break } } if spec == nil { return nil, nil } cpy := spec.Copy() if err := resolveKconfig(cpy); err != nil { return nil, err } kconfig, err := NewMap(cpy) if err != nil { return nil, err } defer closeOnError(kconfig) // Resolve all instructions which load from .kconfig map with actual map // and offset inside it. iter = insns.Iterate() for iter.Next() { meta, _ := iter.Ins.Metadata.Get(kconfigMetaKey{}).(*kconfigMeta) if meta == nil { continue } if meta.Map != spec { return nil, fmt.Errorf("instruction %d: reference to multiple .kconfig maps is not allowed", iter.Index) } if err := iter.Ins.AssociateMap(kconfig); err != nil { return nil, fmt.Errorf("instruction %d: %w", iter.Index, err) } // Encode a map read at the offset of the var in the datasec. iter.Ins.Constant = int64(uint64(meta.Offset) << 32) iter.Ins.Metadata.Set(kconfigMetaKey{}, nil) } return kconfig, nil } func resolveKsymReferences(insns asm.Instructions) error { type fixup struct { *asm.Instruction *ksymMeta } var symbols map[string]uint64 var fixups []fixup iter := insns.Iterate() for iter.Next() { ins := iter.Ins meta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta) if meta == nil { continue } if symbols == nil { symbols = make(map[string]uint64) } symbols[meta.Name] = 0 fixups = append(fixups, fixup{ iter.Ins, meta, }) } if len(symbols) == 0 { return nil } err := kallsyms.AssignAddresses(symbols) // Tolerate ErrRestrictedKernel during initial lookup, user may have all weak // ksyms and a fallback path. if err != nil && !errors.Is(err, ErrRestrictedKernel) { return fmt.Errorf("resolve ksyms: %w", err) } var missing []string for _, fixup := range fixups { addr := symbols[fixup.Name] // A weak ksym variable in eBPF C means its resolution is optional. if addr == 0 && fixup.Binding != elf.STB_WEAK { if !slices.Contains(missing, fixup.Name) { missing = append(missing, fixup.Name) } continue } fixup.Constant = int64(addr) } if len(missing) > 0 { if err != nil { // Program contains required ksyms, return the error from above. return fmt.Errorf("resolve required ksyms: %s: %w", strings.Join(missing, ","), err) } return fmt.Errorf("kernel is missing symbol: %s", strings.Join(missing, ",")) } return nil } golang-github-cilium-ebpf-0.21.0+ds1/linker_test.go000066400000000000000000000055641520243672000220630ustar00rootroot00000000000000package ebpf import ( "errors" "testing" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/testutils" "github.com/go-quicktest/qt" ) func TestFindReferences(t *testing.T) { progs := map[string]*ProgramSpec{ "entrypoint": { Type: SocketFilter, Instructions: asm.Instructions{ // Make sure the call doesn't happen at instruction 0 // to exercise the relative offset calculation. asm.Mov.Reg(asm.R0, asm.R1), asm.Call.Label("my_func"), asm.Return(), }, License: "MIT", }, "my_other_func": { Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 1337, asm.DWord).WithSymbol("my_other_func"), asm.Return(), }, }, "my_func": { Instructions: asm.Instructions{ asm.Call.Label("my_other_func").WithSymbol("my_func"), asm.Return(), }, }, } flattenPrograms(progs, []string{"entrypoint"}) prog, err := newProgram(t, progs["entrypoint"], nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) ret := mustRun(t, prog, nil) if ret != 1337 { t.Errorf("Expected return code 1337, got %d", ret) } } func TestForwardFunctionDeclaration(t *testing.T) { file := testutils.NativeFile(t, "testdata/fwd_decl-%s.elf") coll, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } spec := coll.Programs["call_fwd"] // This program calls an unimplemented forward function declaration. _, err = newProgram(t, spec, nil) if !errors.Is(err, asm.ErrUnsatisfiedProgramReference) { t.Fatal("Expected an error wrapping ErrUnsatisfiedProgramReference, got:", err) } // Append the implementation of fwd(). spec.Instructions = append(spec.Instructions, asm.Mov.Imm32(asm.R0, 23).WithSymbol("fwd"), asm.Return(), ) // The body of the subprog we appended does not come with BTF func_infos, // so the verifier will reject it. Load without BTF. for i, ins := range spec.Instructions { if btf.FuncMetadata(&ins) != nil || ins.Source() != nil { sym := ins.Symbol() ref := ins.Reference() ins.Metadata = asm.Metadata{} spec.Instructions[i] = ins.WithSymbol(sym).WithReference(ref) } } prog, err := newProgram(t, spec, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) ret := mustRun(t, prog, nil) if ret != 23 { t.Fatalf("Expected 23, got %d", ret) } } func TestFlattenInstructionsAllocations(t *testing.T) { name := "entrypoint" instructions := asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), } prog := &ProgramSpec{ Name: name, Instructions: instructions, } progs := map[string]*ProgramSpec{name: prog} refs := make(map[*ProgramSpec][]string) // ensure that flattenInstructions does not allocate memory // if there is no reference for the given program. allocs := testing.AllocsPerRun(5, func() { _ = flattenInstructions(name, progs, refs) }) qt.Assert(t, qt.Equals(allocs, float64(0))) } golang-github-cilium-ebpf-0.21.0+ds1/map.go000066400000000000000000001460621520243672000203140ustar00rootroot00000000000000package ebpf import ( "bytes" "errors" "fmt" "io" "math/rand" "os" "path/filepath" "reflect" "slices" "strings" "sync" "time" "unsafe" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/sysenc" "github.com/cilium/ebpf/internal/unix" ) // Errors returned by Map and MapIterator methods. var ( ErrKeyNotExist = errors.New("key does not exist") ErrKeyExist = errors.New("key already exists") ErrIterationAborted = errors.New("iteration aborted") ErrMapIncompatible = errors.New("map spec is incompatible with existing map") // pre-allocating these errors here since they may get called in hot code paths // and cause unnecessary memory allocations errMapLookupKeyNotExist = fmt.Errorf("lookup: %w", sysErrKeyNotExist) ) // MapOptions control loading a map into the kernel. type MapOptions struct { // The base path to pin maps in if requested via PinByName. // Existing maps will be re-used if they are compatible, otherwise an // error is returned. PinPath string LoadPinOptions LoadPinOptions } // MapID represents the unique ID of an eBPF map type MapID = sys.MapID // MapSpec defines a Map. type MapSpec struct { // Name is passed to the kernel as a debug aid. // // Unsupported characters will be stripped. Name string Type MapType KeySize uint32 ValueSize uint32 MaxEntries uint32 // Flags is passed to the kernel and specifies additional map // creation attributes. Flags uint32 // Automatically pin and load a map from MapOptions.PinPath. // Generates an error if an existing pinned map is incompatible with the MapSpec. Pinning PinType // Specify numa node during map creation // (effective only if sys.BPF_F_NUMA_NODE flag is set, // which can be imported from golang.org/x/sys/unix) NumaNode uint32 // The initial contents of the map. May be nil. Contents []MapKV // InnerMap is used as a template for ArrayOfMaps and HashOfMaps InnerMap *MapSpec // MapExtra is an opaque field whose meaning is map-specific. // // Available from 5.16. MapExtra uint64 // Extra trailing bytes found in the ELF map definition when using structs // larger than libbpf's bpf_map_def. nil if no trailing bytes were present. // Must be nil or empty before instantiating the MapSpec into a Map. Extra *bytes.Reader // The key and value type of this map. May be nil. Key, Value btf.Type // Tags is a list of btf_decl_tag attributes set on the map definition. // // Decorate a map definition with `__attribute__((btf_decl_tag("foo")))`. Tags []string } func (ms *MapSpec) String() string { return fmt.Sprintf("%s(keySize=%d, valueSize=%d, maxEntries=%d, flags=%d)", ms.Type, ms.KeySize, ms.ValueSize, ms.MaxEntries, ms.Flags) } // Copy returns a copy of the spec. // // MapSpec.Contents is a shallow copy. func (ms *MapSpec) Copy() *MapSpec { if ms == nil { return nil } cpy := *ms cpy.Contents = slices.Clone(cpy.Contents) cpy.Key = btf.Copy(cpy.Key) cpy.Value = btf.Copy(cpy.Value) cpy.Tags = slices.Clone(cpy.Tags) if cpy.InnerMap == ms { cpy.InnerMap = &cpy } else { cpy.InnerMap = ms.InnerMap.Copy() } if cpy.Extra != nil { extra := *cpy.Extra cpy.Extra = &extra } return &cpy } // fixupMagicFields fills fields of MapSpec which are usually // left empty in ELF or which depend on runtime information. // // The method doesn't modify Spec, instead returning a copy. // The copy is only performed if fixups are necessary, so callers mustn't mutate // the returned spec. func (spec *MapSpec) fixupMagicFields() (*MapSpec, error) { switch { case spec.Type.canStoreMap(): if spec.ValueSize != 0 && spec.ValueSize != 4 { return nil, errors.New("ValueSize must be zero or four for map of map") } spec = spec.Copy() spec.ValueSize = 4 case spec.Type == PerfEventArray: if spec.KeySize != 0 && spec.KeySize != 4 { return nil, errors.New("KeySize must be zero or four for perf event array") } if spec.ValueSize != 0 && spec.ValueSize != 4 { return nil, errors.New("ValueSize must be zero or four for perf event array") } spec = spec.Copy() spec.KeySize = 4 spec.ValueSize = 4 n, err := PossibleCPU() if err != nil { return nil, fmt.Errorf("fixup perf event array: %w", err) } if n := uint32(n); spec.MaxEntries == 0 || spec.MaxEntries > n { // MaxEntries should be zero most of the time, but there is code // out there which hardcodes large constants. Clamp the number // of entries to the number of CPUs at most. Allow creating maps with // less than n items since some kernel selftests relied on this // behaviour in the past. spec.MaxEntries = n } case spec.Type == CPUMap: n, err := PossibleCPU() if err != nil { return nil, fmt.Errorf("fixup cpu map: %w", err) } if n := uint32(n); spec.MaxEntries == 0 || spec.MaxEntries > n { // Perform clamping similar to PerfEventArray. spec.MaxEntries = n } } return spec, nil } // dataSection returns the contents of a datasec if the MapSpec represents one. func (ms *MapSpec) dataSection() ([]byte, error) { if n := len(ms.Contents); n != 1 { return nil, fmt.Errorf("expected one key, found %d", n) } kv := ms.Contents[0] if key, ok := ms.Contents[0].Key.(uint32); !ok || key != 0 { return nil, fmt.Errorf("expected contents to have key 0") } value, ok := kv.Value.([]byte) if !ok { return nil, fmt.Errorf("value at first map key is %T, not []byte", kv.Value) } return value, nil } // updateDataSection copies the values of variables into MapSpec.Contents[0].Value. // // Only variables declared in sectionName will be updated. func (ms *MapSpec) updateDataSection(vars map[string]*VariableSpec, sectionName string) error { var specs []*VariableSpec for _, vs := range vars { if vs.SectionName != sectionName { continue } specs = append(specs, vs) } if len(specs) == 0 { return nil } data, err := ms.dataSection() if err != nil { return err } // Do not modify the original data slice, ms.Contents is a shallow copy. data = slices.Clone(data) slices.SortFunc(specs, func(a, b *VariableSpec) int { return int(int64(a.Offset) - int64(b.Offset)) }) offset := uint32(0) for _, v := range specs { if v.Offset < offset { return fmt.Errorf("variable %s (offset %d) overlaps with previous variable (offset %d)", v.Name, v.Offset, offset) } end := v.Offset + v.Size() if int(end) > len(data) { return fmt.Errorf("variable %s exceeds map size", v.Name) } copy(data[v.Offset:end], v.Value) offset = end } ms.Contents = []MapKV{{Key: uint32(0), Value: data}} return nil } func (ms *MapSpec) readOnly() bool { return (ms.Flags & sys.BPF_F_RDONLY_PROG) > 0 } func (ms *MapSpec) writeOnly() bool { return (ms.Flags & sys.BPF_F_WRONLY_PROG) > 0 } // MapKV is used to initialize the contents of a Map. type MapKV struct { Key interface{} Value interface{} } // Compatible returns nil if an existing map may be used instead of creating // one from the spec. // // Returns an error wrapping [ErrMapIncompatible] otherwise. func (ms *MapSpec) Compatible(m *Map) error { ms, err := ms.fixupMagicFields() if err != nil { return err } diffs := []string{} if m.typ != ms.Type { diffs = append(diffs, fmt.Sprintf("Type: %s changed to %s", m.typ, ms.Type)) } if m.keySize != ms.KeySize { diffs = append(diffs, fmt.Sprintf("KeySize: %d changed to %d", m.keySize, ms.KeySize)) } if m.valueSize != ms.ValueSize { diffs = append(diffs, fmt.Sprintf("ValueSize: %d changed to %d", m.valueSize, ms.ValueSize)) } if m.maxEntries != ms.MaxEntries { diffs = append(diffs, fmt.Sprintf("MaxEntries: %d changed to %d", m.maxEntries, ms.MaxEntries)) } flags := ms.Flags if ms.Type == DevMap || ms.Type == DevMapHash { // As of 0cdbb4b09a06 ("devmap: Allow map lookups from eBPF") // BPF_F_RDONLY_PROG is set unconditionally for devmaps. Explicitly // allow this mismatch. flags |= (m.flags & sys.BPF_F_RDONLY_PROG) } if m.flags != flags { diffs = append(diffs, fmt.Sprintf("Flags: %d changed to %d", m.flags, flags)) } if len(diffs) == 0 { return nil } return fmt.Errorf("%s: %w", strings.Join(diffs, ", "), ErrMapIncompatible) } // Map represents a Map file descriptor. // // It is not safe to close a map which is used by other goroutines. // // Methods which take interface{} arguments by default encode // them using binary.Read/Write in the machine's native endianness. // // Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler // if you require custom encoding. type Map struct { name string fd *sys.FD typ MapType keySize uint32 valueSize uint32 maxEntries uint32 flags uint32 pinnedPath string // Per CPU maps return values larger than the size in the spec fullValueSize int memory *Memory } // NewMapFromFD creates a [Map] around a raw fd. // // You should not use fd after calling this function. // // Requires at least Linux 4.13. func NewMapFromFD(fd int) (*Map, error) { f, err := sys.NewFD(fd) if err != nil { return nil, err } return newMapFromFD(f) } func newMapFromFD(fd *sys.FD) (*Map, error) { info, err := minimalMapInfoFromFd(fd) if err != nil { fd.Close() return nil, fmt.Errorf("get map info: %w", err) } return newMapFromParts(fd, info.Name, info.Type, info.KeySize, info.ValueSize, info.MaxEntries, info.Flags) } // NewMap creates a new Map. // // It's equivalent to calling NewMapWithOptions with default options. func NewMap(spec *MapSpec) (*Map, error) { return NewMapWithOptions(spec, MapOptions{}) } // NewMapWithOptions creates a new Map. // // Creating a map for the first time will perform feature detection // by creating small, temporary maps. // // The caller is responsible for ensuring the process' rlimit is set // sufficiently high for locking memory during map creation. This can be done // by calling rlimit.RemoveMemlock() prior to calling NewMapWithOptions. // // May return an error wrapping ErrMapIncompatible. func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) { m, err := newMapWithOptions(spec, opts, btf.NewCache()) if err != nil { return nil, fmt.Errorf("creating map: %w", err) } if err := m.finalize(spec); err != nil { m.Close() return nil, fmt.Errorf("populating map: %w", err) } return m, nil } func newMapWithOptions(spec *MapSpec, opts MapOptions, c *btf.Cache) (_ *Map, err error) { closeOnError := func(c io.Closer) { if err != nil { c.Close() } } switch spec.Pinning { case PinByName: if spec.Name == "" { return nil, fmt.Errorf("pin by name: missing Name") } if opts.PinPath == "" { return nil, fmt.Errorf("pin by name: missing MapOptions.PinPath") } path := filepath.Join(opts.PinPath, spec.Name) m, err := LoadPinnedMap(path, &opts.LoadPinOptions) if errors.Is(err, unix.ENOENT) { break } if err != nil { return nil, fmt.Errorf("load pinned map: %w", err) } defer closeOnError(m) if err := spec.Compatible(m); err != nil { return nil, fmt.Errorf("use pinned map %s: %w", spec.Name, err) } return m, nil case PinNone: // Nothing to do here default: return nil, fmt.Errorf("pin type %d: %w", int(spec.Pinning), ErrNotSupported) } var innerFd *sys.FD if spec.Type.canStoreMap() { if spec.InnerMap == nil { return nil, fmt.Errorf("%s requires InnerMap", spec.Type) } if spec.InnerMap.Pinning != PinNone { return nil, errors.New("inner maps cannot be pinned") } template, err := spec.InnerMap.createMap(nil, c) if err != nil { return nil, fmt.Errorf("inner map: %w", err) } defer template.Close() // Intentionally skip populating and freezing (finalizing) // the inner map template since it will be removed shortly. innerFd = template.fd } m, err := spec.createMap(innerFd, c) if err != nil { return nil, err } defer closeOnError(m) if spec.Pinning == PinByName { path := filepath.Join(opts.PinPath, spec.Name) if err := m.Pin(path); err != nil { return nil, fmt.Errorf("pin map to %s: %w", path, err) } } return m, nil } // Memory returns a memory-mapped region for the Map. The Map must have been // created with the BPF_F_MMAPABLE flag. Repeated calls to Memory return the // same mapping. Callers are responsible for coordinating access to Memory. func (m *Map) Memory() (*Memory, error) { if m.memory != nil { return m.memory, nil } if m.flags&sys.BPF_F_MMAPABLE == 0 { return nil, fmt.Errorf("Map was not created with the BPF_F_MMAPABLE flag: %w", ErrNotSupported) } size, err := m.memorySize() if err != nil { return nil, err } mm, err := newMemory(m.FD(), size) if err != nil { return nil, fmt.Errorf("creating new Memory: %w", err) } m.memory = mm return mm, nil } // unsafeMemory returns a heap-mapped memory region for the Map. The Map must // have been created with the BPF_F_MMAPABLE flag. Repeated calls to Memory // return the same mapping. Callers are responsible for coordinating access to // Memory. func (m *Map) unsafeMemory() (*Memory, error) { if m.memory != nil { if !m.memory.heap { return nil, errors.New("unsafeMemory would return existing non-heap memory") } return m.memory, nil } if m.flags&sys.BPF_F_MMAPABLE == 0 { return nil, fmt.Errorf("Map was not created with the BPF_F_MMAPABLE flag: %w", ErrNotSupported) } size, err := m.memorySize() if err != nil { return nil, err } mm, err := newUnsafeMemory(m.FD(), size) if err != nil { return nil, fmt.Errorf("creating new Memory: %w", err) } m.memory = mm return mm, nil } func (m *Map) memorySize() (int, error) { switch m.Type() { case Array: // In Arrays, values are always laid out on 8-byte boundaries regardless of // architecture. Multiply by MaxEntries and align the result to the host's // page size. size := int(internal.Align(m.ValueSize(), 8) * m.MaxEntries()) size = internal.Align(size, os.Getpagesize()) return size, nil case Arena: // For Arenas, MaxEntries denotes the maximum number of pages available to // the arena. return int(m.MaxEntries()) * os.Getpagesize(), nil } return 0, fmt.Errorf("determine memory size of map type %s: %w", m.Type(), ErrNotSupported) } // createMap validates the spec's properties and creates the map in the kernel // using the given opts. It does not populate or freeze the map. func (spec *MapSpec) createMap(inner *sys.FD, c *btf.Cache) (_ *Map, err error) { closeOnError := func(closer io.Closer) { if err != nil { closer.Close() } } // Kernels 4.13 through 5.4 used a struct bpf_map_def that contained // additional 'inner_map_idx' and later 'numa_node' fields. // In order to support loading these definitions, tolerate the presence of // extra bytes, but require them to be zeroes. if spec.Extra != nil { if _, err := io.Copy(internal.DiscardZeroes{}, spec.Extra); err != nil { return nil, errors.New("extra contains unhandled non-zero bytes, drain before creating map") } } spec, err = spec.fixupMagicFields() if err != nil { return nil, err } p, sysMapType := platform.DecodeConstant(spec.Type) if p != platform.Native { return nil, fmt.Errorf("map type %s (%s): %w", spec.Type, p, internal.ErrNotSupportedOnOS) } attr := sys.MapCreateAttr{ MapName: maybeFillObjName(spec.Name), MapType: sys.MapType(sysMapType), KeySize: spec.KeySize, ValueSize: spec.ValueSize, MaxEntries: spec.MaxEntries, MapFlags: spec.Flags, NumaNode: spec.NumaNode, MapExtra: spec.MapExtra, } if inner != nil { attr.InnerMapFd = inner.Uint() } if spec.Key != nil || spec.Value != nil { handle, keyTypeID, valueTypeID, err := btf.MarshalMapKV(spec.Key, spec.Value) if err != nil && !errors.Is(err, btf.ErrNotSupported) { return nil, fmt.Errorf("load BTF: %w", err) } if handle != nil { defer handle.Close() // Use BTF k/v during map creation. attr.BtfFd = uint32(handle.FD()) attr.BtfKeyTypeId = keyTypeID attr.BtfValueTypeId = valueTypeID } if spec.Type == StructOpsMap { if handle == nil { return nil, fmt.Errorf("struct_ops requires BTF") } localValue, ok := btf.As[*btf.Struct](spec.Value) if !ok { return nil, fmt.Errorf("struct_ops: value must be struct") } targetValue, targetID, module, err := structOpsFindTarget(localValue, c) if err != nil { return nil, fmt.Errorf("struct_ops: %w", err) } defer module.Close() spec = spec.Copy() spec.ValueSize = targetValue.Size attr.ValueSize = targetValue.Size attr.BtfVmlinuxValueTypeId = targetID if module != nil { // BPF_F_VTYPE_BTF_OBJ_FD is required if the type comes from a module attr.MapFlags |= sys.BPF_F_VTYPE_BTF_OBJ_FD // set FD for the kernel module attr.ValueTypeBtfObjFd = int32(module.FD()) } // StructOpsMap forbids passing BtfKeyTypeId or BtfValueTypeId, but // requires BtfFd. Do the simple thing and just zero out the fields. // See https://github.com/torvalds/linux/blob/9b332cece987ee1790b2ed4c989e28162fa47860/kernel/bpf/syscall.c#L1382-L1384 attr.BtfKeyTypeId = 0 attr.BtfValueTypeId = 0 } } fd, err := sys.MapCreate(&attr) // Some map types don't support BTF k/v in earlier kernel versions. // Remove BTF metadata and retry map creation. if (errors.Is(err, sys.ENOTSUPP) || errors.Is(err, unix.EINVAL)) && attr.BtfFd != 0 { attr.BtfFd, attr.BtfKeyTypeId, attr.BtfValueTypeId = 0, 0, 0 fd, err = sys.MapCreate(&attr) } if err != nil { return nil, handleMapCreateError(attr, spec, err) } defer closeOnError(fd) m, err := newMapFromParts(fd, spec.Name, spec.Type, spec.KeySize, spec.ValueSize, spec.MaxEntries, spec.Flags) if err != nil { return nil, fmt.Errorf("map create: %w", err) } return m, nil } func handleMapCreateError(attr sys.MapCreateAttr, spec *MapSpec, err error) error { if platform.IsWindows { if errors.Is(err, unix.EINVAL) && attr.MapFlags != 0 { return fmt.Errorf("map create: flags: %w", internal.ErrNotSupportedOnOS) } return err } if errors.Is(err, unix.EPERM) { return fmt.Errorf("map create: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err) } if errors.Is(err, unix.EINVAL) { if spec.MaxEntries == 0 { return fmt.Errorf("map create: %w (MaxEntries may be incorrectly set to zero)", err) } if spec.Type == UnspecifiedMap { return fmt.Errorf("map create: cannot use type %s", UnspecifiedMap) } if spec.Flags&sys.BPF_F_NO_PREALLOC != 0 && !spec.Type.mustHaveNoPrealloc() { return fmt.Errorf("map create: %w (BPF_F_NO_PREALLOC flag may be incompatible with map type %s)", err, spec.Type) } if spec.Flags&sys.BPF_F_NO_PREALLOC == 0 && spec.Type.mustHaveNoPrealloc() { return fmt.Errorf("map create: %w (BPF_F_NO_PREALLOC flag may need to be set for map type %s)", err, spec.Type) } } if spec.Type.canStoreMap() { if haveFeatErr := haveNestedMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } if spec.readOnly() || spec.writeOnly() { if haveFeatErr := haveMapMutabilityModifiers(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } if spec.Flags&sys.BPF_F_MMAPABLE > 0 { if haveFeatErr := haveMmapableMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } if spec.Flags&sys.BPF_F_INNER_MAP > 0 { if haveFeatErr := haveInnerMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } if spec.Flags&sys.BPF_F_NO_PREALLOC > 0 { if haveFeatErr := haveNoPreallocMaps(); haveFeatErr != nil { return fmt.Errorf("map create: %w", haveFeatErr) } } // BPF_MAP_TYPE_RINGBUF's max_entries must be a power-of-2 multiple of kernel's page size. if errors.Is(err, unix.EINVAL) && (attr.MapType == sys.BPF_MAP_TYPE_RINGBUF || attr.MapType == sys.BPF_MAP_TYPE_USER_RINGBUF) { pageSize := uint32(os.Getpagesize()) maxEntries := attr.MaxEntries if maxEntries%pageSize != 0 || !internal.IsPow(maxEntries) { return fmt.Errorf("map create: %w (ring map size %d not a multiple of page size %d)", err, maxEntries, pageSize) } } return fmt.Errorf("map create: %w", err) } // newMapFromParts allocates and returns a new Map structure. // Sets the fullValueSize on per-CPU maps. func newMapFromParts(fd *sys.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) { m := &Map{ name, fd, typ, keySize, valueSize, maxEntries, flags, "", int(valueSize), nil, } if !typ.hasPerCPUValue() { return m, nil } possibleCPUs, err := PossibleCPU() if err != nil { return nil, err } m.fullValueSize = int(internal.Align(valueSize, 8)) * possibleCPUs return m, nil } func (m *Map) String() string { if m.name != "" { return fmt.Sprintf("%s(%s)#%v", m.typ, m.name, m.fd) } return fmt.Sprintf("%s#%v", m.typ, m.fd) } // Type returns the underlying type of the map. func (m *Map) Type() MapType { return m.typ } // KeySize returns the size of the map key in bytes. func (m *Map) KeySize() uint32 { return m.keySize } // ValueSize returns the size of the map value in bytes. func (m *Map) ValueSize() uint32 { return m.valueSize } // MaxEntries returns the maximum number of elements the map can hold. func (m *Map) MaxEntries() uint32 { return m.maxEntries } // Flags returns the flags of the map. func (m *Map) Flags() uint32 { return m.flags } // Info returns metadata about the map. This was first introduced in Linux 4.5, // but newer kernels support more MapInfo fields with the introduction of more // features. See [MapInfo] and its methods for more details. // // Returns an error wrapping [ErrNotSupported] if the kernel supports neither // BPF_OBJ_GET_INFO_BY_FD nor reading map information from /proc/self/fdinfo. func (m *Map) Info() (*MapInfo, error) { return newMapInfoFromFd(m.fd) } // Handle returns a reference to the Map's type information in the kernel. // // Returns [ErrNotSupported] if the kernel has no BTF support, or if there is no // BTF associated with the Map. func (m *Map) Handle() (*btf.Handle, error) { info, err := m.Info() if err != nil { return nil, err } id, ok := info.BTFID() if !ok { return nil, fmt.Errorf("map %s: retrieve BTF ID: %w", m, ErrNotSupported) } return btf.NewHandleFromID(id) } // MapLookupFlags controls the behaviour of the map lookup calls. type MapLookupFlags uint64 // LookupLock look up the value of a spin-locked map. const LookupLock MapLookupFlags = sys.BPF_F_LOCK // Lookup retrieves a value from a Map. // // Calls Close() on valueOut if it is of type **Map or **Program, // and *valueOut is not nil. // // Returns an error if the key doesn't exist, see ErrKeyNotExist. func (m *Map) Lookup(key, valueOut interface{}) error { return m.LookupWithFlags(key, valueOut, 0) } // LookupWithFlags retrieves a value from a Map with flags. // // Passing LookupLock flag will look up the value of a spin-locked // map without returning the lock. This must be specified if the // elements contain a spinlock. // // Calls Close() on valueOut if it is of type **Map or **Program, // and *valueOut is not nil. // // Returns an error if the key doesn't exist, see ErrKeyNotExist. func (m *Map) LookupWithFlags(key, valueOut interface{}, flags MapLookupFlags) error { if m.typ.hasPerCPUValue() { return m.lookupPerCPU(key, valueOut, flags) } valueBytes := makeMapSyscallOutput(valueOut, m.fullValueSize) if err := m.lookup(key, valueBytes.Pointer(), flags); err != nil { return err } return m.unmarshalValue(valueOut, valueBytes) } // LookupAndDelete retrieves and deletes a value from a Map. // // Returns ErrKeyNotExist if the key doesn't exist. func (m *Map) LookupAndDelete(key, valueOut interface{}) error { return m.LookupAndDeleteWithFlags(key, valueOut, 0) } // LookupAndDeleteWithFlags retrieves and deletes a value from a Map. // // Passing LookupLock flag will look up and delete the value of a spin-locked // map without returning the lock. This must be specified if the elements // contain a spinlock. // // Returns ErrKeyNotExist if the key doesn't exist. func (m *Map) LookupAndDeleteWithFlags(key, valueOut interface{}, flags MapLookupFlags) error { if m.typ.hasPerCPUValue() { return m.lookupAndDeletePerCPU(key, valueOut, flags) } valueBytes := makeMapSyscallOutput(valueOut, m.fullValueSize) if err := m.lookupAndDelete(key, valueBytes.Pointer(), flags); err != nil { return err } return m.unmarshalValue(valueOut, valueBytes) } // LookupBytes gets a value from Map. // // Returns a nil value if a key doesn't exist. func (m *Map) LookupBytes(key interface{}) ([]byte, error) { valueBytes := make([]byte, m.fullValueSize) valuePtr := sys.UnsafeSlicePointer(valueBytes) err := m.lookup(key, valuePtr, 0) if errors.Is(err, ErrKeyNotExist) { return nil, nil } return valueBytes, err } func (m *Map) lookupPerCPU(key, valueOut any, flags MapLookupFlags) error { slice, err := ensurePerCPUSlice(valueOut) if err != nil { return err } valueBytes := make([]byte, m.fullValueSize) if err := m.lookup(key, sys.UnsafeSlicePointer(valueBytes), flags); err != nil { return err } return unmarshalPerCPUValue(slice, int(m.valueSize), valueBytes) } func (m *Map) lookup(key interface{}, valueOut sys.Pointer, flags MapLookupFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } attr := sys.MapLookupElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, Value: valueOut, Flags: uint64(flags), } if err = sys.MapLookupElem(&attr); err != nil { if errors.Is(err, unix.ENOENT) { return errMapLookupKeyNotExist } return fmt.Errorf("lookup: %w", wrapMapError(err)) } return nil } func (m *Map) lookupAndDeletePerCPU(key, valueOut any, flags MapLookupFlags) error { slice, err := ensurePerCPUSlice(valueOut) if err != nil { return err } valueBytes := make([]byte, m.fullValueSize) if err := m.lookupAndDelete(key, sys.UnsafeSlicePointer(valueBytes), flags); err != nil { return err } return unmarshalPerCPUValue(slice, int(m.valueSize), valueBytes) } // ensurePerCPUSlice allocates a slice for a per-CPU value if necessary. func ensurePerCPUSlice(sliceOrPtr any) (any, error) { sliceOrPtrType := reflect.TypeOf(sliceOrPtr) if sliceOrPtrType.Kind() == reflect.Slice { // The target is a slice, the caller is responsible for ensuring that // size is correct. return sliceOrPtr, nil } slicePtrType := sliceOrPtrType if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice { return nil, fmt.Errorf("per-cpu value requires a slice or a pointer to slice") } possibleCPUs, err := PossibleCPU() if err != nil { return nil, err } sliceType := slicePtrType.Elem() slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs) sliceElemType := sliceType.Elem() sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr reflect.ValueOf(sliceOrPtr).Elem().Set(slice) if !sliceElemIsPointer { return slice.Interface(), nil } sliceElemType = sliceElemType.Elem() for i := 0; i < possibleCPUs; i++ { newElem := reflect.New(sliceElemType) slice.Index(i).Set(newElem) } return slice.Interface(), nil } func (m *Map) lookupAndDelete(key any, valuePtr sys.Pointer, flags MapLookupFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } attr := sys.MapLookupAndDeleteElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, Value: valuePtr, Flags: uint64(flags), } if err := sys.MapLookupAndDeleteElem(&attr); err != nil { return fmt.Errorf("lookup and delete: %w", wrapMapError(err)) } return nil } // MapUpdateFlags controls the behaviour of the Map.Update call. // // The exact semantics depend on the specific MapType. type MapUpdateFlags uint64 const ( // UpdateAny creates a new element or update an existing one. UpdateAny MapUpdateFlags = iota // UpdateNoExist creates a new element. UpdateNoExist MapUpdateFlags = 1 << (iota - 1) // UpdateExist updates an existing element. UpdateExist // UpdateLock updates elements under bpf_spin_lock. UpdateLock ) // Put replaces or creates a value in map. // // It is equivalent to calling Update with UpdateAny. func (m *Map) Put(key, value interface{}) error { return m.Update(key, value, UpdateAny) } // Update changes the value of a key. func (m *Map) Update(key, value any, flags MapUpdateFlags) error { if m.typ.hasPerCPUValue() { return m.updatePerCPU(key, value, flags) } valuePtr, err := m.marshalValue(value) if err != nil { return fmt.Errorf("marshal value: %w", err) } return m.update(key, valuePtr, flags) } func (m *Map) updatePerCPU(key, value any, flags MapUpdateFlags) error { valuePtr, err := marshalPerCPUValue(value, int(m.valueSize)) if err != nil { return fmt.Errorf("marshal value: %w", err) } return m.update(key, valuePtr, flags) } func (m *Map) update(key any, valuePtr sys.Pointer, flags MapUpdateFlags) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("marshal key: %w", err) } attr := sys.MapUpdateElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, Value: valuePtr, Flags: uint64(flags), } if err = sys.MapUpdateElem(&attr); err != nil { return fmt.Errorf("update: %w", wrapMapError(err)) } return nil } // Delete removes a value. // // Returns ErrKeyNotExist if the key does not exist. func (m *Map) Delete(key interface{}) error { keyPtr, err := m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } attr := sys.MapDeleteElemAttr{ MapFd: m.fd.Uint(), Key: keyPtr, } if err = sys.MapDeleteElem(&attr); err != nil { return fmt.Errorf("delete: %w", wrapMapError(err)) } return nil } // NextKey finds the key following an initial key. // // See NextKeyBytes for details. // // Returns ErrKeyNotExist if there is no next key. func (m *Map) NextKey(key, nextKeyOut interface{}) error { nextKeyBytes := makeMapSyscallOutput(nextKeyOut, int(m.keySize)) if err := m.nextKey(key, nextKeyBytes.Pointer()); err != nil { return err } if err := nextKeyBytes.Unmarshal(nextKeyOut); err != nil { return fmt.Errorf("can't unmarshal next key: %w", err) } return nil } // NextKeyBytes returns the key following an initial key as a byte slice. // // Passing nil will return the first key. // // Use Iterate if you want to traverse all entries in the map. // // Returns nil if there are no more keys. func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { nextKey := make([]byte, m.keySize) nextKeyPtr := sys.UnsafeSlicePointer(nextKey) err := m.nextKey(key, nextKeyPtr) if errors.Is(err, ErrKeyNotExist) { return nil, nil } return nextKey, err } func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error { var ( keyPtr sys.Pointer err error ) if key != nil { keyPtr, err = m.marshalKey(key) if err != nil { return fmt.Errorf("can't marshal key: %w", err) } } attr := sys.MapGetNextKeyAttr{ MapFd: m.fd.Uint(), Key: keyPtr, NextKey: nextKeyOut, } if err = sys.MapGetNextKey(&attr); err != nil { // Kernels 4.4.131 and earlier return EFAULT instead of a pointer to the // first map element when a nil key pointer is specified. if platform.IsLinux && key == nil && errors.Is(err, unix.EFAULT) { var guessKey []byte guessKey, err = m.guessNonExistentKey() if err != nil { return err } // Retry the syscall with a valid non-existing key. attr.Key = sys.UnsafeSlicePointer(guessKey) if err = sys.MapGetNextKey(&attr); err == nil { return nil } } return fmt.Errorf("next key: %w", wrapMapError(err)) } return nil } var mmapProtectedPage = sync.OnceValues(func() ([]byte, error) { return unix.Mmap(-1, 0, os.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_SHARED) }) // guessNonExistentKey attempts to perform a map lookup that returns ENOENT. // This is necessary on kernels before 4.4.132, since those don't support // iterating maps from the start by providing an invalid key pointer. func (m *Map) guessNonExistentKey() ([]byte, error) { // Map a protected page and use that as the value pointer. This saves some // work copying out the value, which we're not interested in. page, err := mmapProtectedPage() if err != nil { return nil, err } valuePtr := sys.UnsafeSlicePointer(page) randKey := make([]byte, int(m.keySize)) for i := 0; i < 4; i++ { switch i { // For hash maps, the 0 key is less likely to be occupied. They're often // used for storing data related to pointers, and their access pattern is // generally scattered across the keyspace. case 0: // An all-0xff key is guaranteed to be out of bounds of any array, since // those have a fixed key size of 4 bytes. The only corner case being // arrays with 2^32 max entries, but those are prohibitively expensive // in many environments. case 1: for r := range randKey { randKey[r] = 0xff } // Inspired by BCC, 0x55 is an alternating binary pattern (0101), so // is unlikely to be taken. case 2: for r := range randKey { randKey[r] = 0x55 } // Last ditch effort, generate a random key. case 3: rand.New(rand.NewSource(time.Now().UnixNano())).Read(randKey) } err := m.lookup(randKey, valuePtr, 0) if errors.Is(err, ErrKeyNotExist) { return randKey, nil } } return nil, errors.New("couldn't find non-existing key") } // BatchLookup looks up many elements in a map at once. // // "keysOut" and "valuesOut" must be of type slice, a pointer // to a slice or buffer will not work. // "cursor" is an pointer to an opaque handle. It must be non-nil. Pass // "cursor" to subsequent calls of this function to continue the batching // operation in the case of chunking. // // Warning: This API is not very safe to use as the kernel implementation for // batching relies on the user to be aware of subtle details with regarding to // different map type implementations. // // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results // are returned. It should be used to evaluate when lookup is "done". func (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { n, err := m.batchLookup(sys.BPF_MAP_LOOKUP_BATCH, cursor, keysOut, valuesOut, opts) if err != nil { return n, fmt.Errorf("map batch lookup: %w", err) } return n, nil } // BatchLookupAndDelete looks up many elements in a map at once, // // It then deletes all those elements. // "keysOut" and "valuesOut" must be of type slice, a pointer // to a slice or buffer will not work. // "cursor" is an pointer to an opaque handle. It must be non-nil. Pass // "cursor" to subsequent calls of this function to continue the batching // operation in the case of chunking. // // Warning: This API is not very safe to use as the kernel implementation for // batching relies on the user to be aware of subtle details with regarding to // different map type implementations. // // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results // are returned. It should be used to evaluate when lookup is "done". func (m *Map) BatchLookupAndDelete(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { n, err := m.batchLookup(sys.BPF_MAP_LOOKUP_AND_DELETE_BATCH, cursor, keysOut, valuesOut, opts) if err != nil { return n, fmt.Errorf("map batch lookup and delete: %w", err) } return n, nil } // MapBatchCursor represents a starting point for a batch operation. type MapBatchCursor struct { m *Map opaque []byte } func (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { if m.typ.hasPerCPUValue() { return m.batchLookupPerCPU(cmd, cursor, keysOut, valuesOut, opts) } count, err := batchCount(keysOut, valuesOut) if err != nil { return 0, err } valueBuf := sysenc.SyscallOutput(valuesOut, count*int(m.fullValueSize)) n, sysErr := m.batchLookupCmd(cmd, cursor, count, keysOut, valueBuf.Pointer(), opts) if errors.Is(sysErr, unix.ENOSPC) { // Hash tables return ENOSPC when the size of the batch is smaller than // any bucket. return n, fmt.Errorf("%w (batch size too small?)", sysErr) } else if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) { return 0, sysErr } err = valueBuf.Unmarshal(valuesOut) if err != nil { return 0, err } return n, sysErr } func (m *Map) batchLookupPerCPU(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { count, err := sliceLen(keysOut) if err != nil { return 0, fmt.Errorf("keys: %w", err) } valueBuf := sysenc.SyscallOutput(valuesOut, count*int(m.fullValueSize)) n, sysErr := m.batchLookupCmd(cmd, cursor, count, keysOut, valueBuf.Pointer(), opts) if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) { return 0, sysErr } if bytesBuf := valueBuf.Bytes(); bytesBuf != nil { err = unmarshalBatchPerCPUValue(valuesOut, count, int(m.valueSize), bytesBuf) if err != nil { return 0, err } } return n, sysErr } func (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, keysOut any, valuePtr sys.Pointer, opts *BatchOptions) (int, error) { // * generic_map_lookup_batch requires that batch_out is key_size bytes. // This is used by array and LPM maps. // // * __htab_map_lookup_and_delete_batch requires u32. This is used by the // various hash maps. // // Use a minimum of 4 bytes to avoid having to distinguish between the two. cursorLen := max(int(m.keySize), 4) inBatch := cursor.opaque if inBatch == nil { // This is the first lookup, allocate a buffer to hold the cursor. cursor.opaque = make([]byte, cursorLen) cursor.m = m } else if cursor.m != m { // Prevent reuse of a cursor across maps. First, it's unlikely to work. // Second, the maps may require different cursorLen and cursor.opaque // may therefore be too short. This could lead to the kernel clobbering // user space memory. return 0, errors.New("a cursor may not be reused across maps") } if err := haveBatchAPI(); err != nil { return 0, err } keyBuf := sysenc.SyscallOutput(keysOut, count*int(m.keySize)) attr := sys.MapLookupBatchAttr{ MapFd: m.fd.Uint(), Keys: keyBuf.Pointer(), Values: valuePtr, Count: uint32(count), InBatch: sys.UnsafeSlicePointer(inBatch), OutBatch: sys.UnsafeSlicePointer(cursor.opaque), } if opts != nil { attr.ElemFlags = opts.ElemFlags attr.Flags = opts.Flags } _, sysErr := sys.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) sysErr = wrapMapError(sysErr) if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) { return 0, sysErr } if err := keyBuf.Unmarshal(keysOut); err != nil { return 0, err } return int(attr.Count), sysErr } // BatchUpdate updates the map with multiple keys and values // simultaneously. // "keys" and "values" must be of type slice, a pointer // to a slice or buffer will not work. func (m *Map) BatchUpdate(keys, values interface{}, opts *BatchOptions) (int, error) { if m.typ.hasPerCPUValue() { return m.batchUpdatePerCPU(keys, values, opts) } count, err := batchCount(keys, values) if err != nil { return 0, err } valuePtr, err := marshalMapSyscallInput(values, count*int(m.valueSize)) if err != nil { return 0, err } return m.batchUpdate(count, keys, valuePtr, opts) } func (m *Map) batchUpdate(count int, keys any, valuePtr sys.Pointer, opts *BatchOptions) (int, error) { keyPtr, err := marshalMapSyscallInput(keys, count*int(m.keySize)) if err != nil { return 0, err } attr := sys.MapUpdateBatchAttr{ MapFd: m.fd.Uint(), Keys: keyPtr, Values: valuePtr, Count: uint32(count), } if opts != nil { attr.ElemFlags = opts.ElemFlags attr.Flags = opts.Flags } err = sys.MapUpdateBatch(&attr) if err != nil { if haveFeatErr := haveBatchAPI(); haveFeatErr != nil { return 0, haveFeatErr } return int(attr.Count), fmt.Errorf("batch update: %w", wrapMapError(err)) } return int(attr.Count), nil } func (m *Map) batchUpdatePerCPU(keys, values any, opts *BatchOptions) (int, error) { count, err := sliceLen(keys) if err != nil { return 0, fmt.Errorf("keys: %w", err) } valueBuf, err := marshalBatchPerCPUValue(values, count, int(m.valueSize)) if err != nil { return 0, err } return m.batchUpdate(count, keys, sys.UnsafeSlicePointer(valueBuf), opts) } // BatchDelete batch deletes entries in the map by keys. // "keys" must be of type slice, a pointer to a slice or buffer will not work. func (m *Map) BatchDelete(keys interface{}, opts *BatchOptions) (int, error) { count, err := sliceLen(keys) if err != nil { return 0, fmt.Errorf("keys: %w", err) } keyPtr, err := marshalMapSyscallInput(keys, count*int(m.keySize)) if err != nil { return 0, fmt.Errorf("cannot marshal keys: %v", err) } attr := sys.MapDeleteBatchAttr{ MapFd: m.fd.Uint(), Keys: keyPtr, Count: uint32(count), } if opts != nil { attr.ElemFlags = opts.ElemFlags attr.Flags = opts.Flags } if err = sys.MapDeleteBatch(&attr); err != nil { if haveFeatErr := haveBatchAPI(); haveFeatErr != nil { return 0, haveFeatErr } return int(attr.Count), fmt.Errorf("batch delete: %w", wrapMapError(err)) } return int(attr.Count), nil } func batchCount(keys, values any) (int, error) { keysLen, err := sliceLen(keys) if err != nil { return 0, fmt.Errorf("keys: %w", err) } valuesLen, err := sliceLen(values) if err != nil { return 0, fmt.Errorf("values: %w", err) } if keysLen != valuesLen { return 0, fmt.Errorf("keys and values must have the same length") } return keysLen, nil } // Iterate traverses a map. // // It's safe to create multiple iterators at the same time. // // It's not possible to guarantee that all keys in a map will be // returned if there are concurrent modifications to the map. func (m *Map) Iterate() *MapIterator { return newMapIterator(m) } // Close the Map's underlying file descriptor, which could unload the // Map from the kernel if it is not pinned or in use by a loaded Program. func (m *Map) Close() error { if m == nil { // This makes it easier to clean up when iterating maps // of maps / programs. return nil } return m.fd.Close() } // FD gets the file descriptor of the Map. // // Calling this function is invalid after Close has been called. func (m *Map) FD() int { return m.fd.Int() } // Clone creates a duplicate of the Map. // // Closing the duplicate does not affect the original, and vice versa. // Changes made to the map are reflected by both instances however. // If the original map was pinned, the cloned map will not be pinned by default. // // Cloning a nil Map returns nil. func (m *Map) Clone() (*Map, error) { if m == nil { return nil, nil } dup, err := m.fd.Dup() if err != nil { return nil, fmt.Errorf("can't clone map: %w", err) } return &Map{ m.name, dup, m.typ, m.keySize, m.valueSize, m.maxEntries, m.flags, "", m.fullValueSize, nil, }, nil } // Pin persists the map on the BPF virtual file system past the lifetime of // the process that created it . // // Calling Pin on a previously pinned map will overwrite the path, except when // the new path already exists. Re-pinning across filesystems is not supported. // You can Clone a map to pin it to a different path. // // This requires bpffs to be mounted above fileName. // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd func (m *Map) Pin(fileName string) error { if err := sys.Pin(m.pinnedPath, fileName, m.fd); err != nil { return err } m.pinnedPath = fileName return nil } // Unpin removes the persisted state for the map from the BPF virtual filesystem. // // Failed calls to Unpin will not alter the state returned by IsPinned. // // Unpinning an unpinned Map returns nil. func (m *Map) Unpin() error { if err := sys.Unpin(m.pinnedPath); err != nil { return err } m.pinnedPath = "" return nil } // IsPinned returns true if the map has a non-empty pinned path. func (m *Map) IsPinned() bool { return m.pinnedPath != "" } // Freeze prevents a map to be modified from user space. // // It makes no changes to kernel-side restrictions. func (m *Map) Freeze() error { attr := sys.MapFreezeAttr{ MapFd: m.fd.Uint(), } if err := sys.MapFreeze(&attr); err != nil { if haveFeatErr := haveMapMutabilityModifiers(); haveFeatErr != nil { return fmt.Errorf("can't freeze map: %w", haveFeatErr) } return fmt.Errorf("can't freeze map: %w", err) } return nil } // finalize populates the Map according to the Contents specified // in spec and freezes the Map if requested by spec. func (m *Map) finalize(spec *MapSpec) error { for _, kv := range spec.Contents { if err := m.Put(kv.Key, kv.Value); err != nil { return fmt.Errorf("putting value: key %v: %w", kv.Key, err) } } if isConstantDataSection(spec.Name) || isKconfigSection(spec.Name) { if err := m.Freeze(); err != nil { return fmt.Errorf("freezing map: %w", err) } } return nil } func (m *Map) marshalKey(data interface{}) (sys.Pointer, error) { if data == nil { if m.keySize == 0 { // Queues have a key length of zero, so passing nil here is valid. return sys.UnsafePointer(nil), nil } return sys.Pointer{}, errors.New("can't use nil as key of map") } return marshalMapSyscallInput(data, int(m.keySize)) } func (m *Map) marshalValue(data interface{}) (sys.Pointer, error) { var ( buf []byte err error ) switch value := data.(type) { case *Map: if !m.typ.canStoreMap() { return sys.Pointer{}, fmt.Errorf("can't store map in %s", m.typ) } buf, err = marshalMap(value, int(m.valueSize)) case *Program: if !m.typ.canStoreProgram() { return sys.Pointer{}, fmt.Errorf("can't store program in %s", m.typ) } buf, err = marshalProgram(value, int(m.valueSize)) default: return marshalMapSyscallInput(data, int(m.valueSize)) } if err != nil { return sys.Pointer{}, err } return sys.UnsafeSlicePointer(buf), nil } func (m *Map) unmarshalValue(value any, buf sysenc.Buffer) error { switch value := value.(type) { case **Map: if !m.typ.canStoreMap() { return fmt.Errorf("can't read a map from %s", m.typ) } other, err := unmarshalMap(buf) if err != nil { return err } // The caller might close the map externally, so ignore errors. _ = (*value).Close() *value = other return nil case *Map: if !m.typ.canStoreMap() { return fmt.Errorf("can't read a map from %s", m.typ) } return errors.New("require pointer to *Map") case **Program: if !m.typ.canStoreProgram() { return fmt.Errorf("can't read a program from %s", m.typ) } other, err := unmarshalProgram(buf) if err != nil { return err } // The caller might close the program externally, so ignore errors. _ = (*value).Close() *value = other return nil case *Program: if !m.typ.canStoreProgram() { return fmt.Errorf("can't read a program from %s", m.typ) } return errors.New("require pointer to *Program") } return buf.Unmarshal(value) } // LoadPinnedMap opens a Map from a pin (file) on the BPF virtual filesystem. // // Requires at least Linux 4.5. func LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) { fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) if err != nil { return nil, err } if typ != sys.BPF_TYPE_MAP { _ = fd.Close() return nil, fmt.Errorf("%s is not a Map", fileName) } m, err := newMapFromFD(fd) if err == nil { m.pinnedPath = fileName } return m, err } // unmarshalMap creates a map from a map ID encoded in host endianness. func unmarshalMap(buf sysenc.Buffer) (*Map, error) { var id uint32 if err := buf.Unmarshal(&id); err != nil { return nil, err } return NewMapFromID(MapID(id)) } // marshalMap marshals the fd of a map into a buffer in host endianness. func marshalMap(m *Map, length int) ([]byte, error) { if m == nil { return nil, errors.New("can't marshal a nil Map") } if length != 4 { return nil, fmt.Errorf("can't marshal map to %d bytes", length) } buf := make([]byte, 4) internal.NativeEndian.PutUint32(buf, m.fd.Uint()) return buf, nil } // MapIterator iterates a Map. // // See Map.Iterate. type MapIterator struct { target *Map // Temporary storage to avoid allocations in Next(). This is any instead // of []byte to avoid allocations. cursor any count, maxEntries uint32 done bool err error } func newMapIterator(target *Map) *MapIterator { return &MapIterator{ target: target, maxEntries: target.maxEntries, } } // Next decodes the next key and value. // // Iterating a hash map from which keys are being deleted is not // safe. You may see the same key multiple times. Iteration may // also abort with an error, see IsIterationAborted. // // Returns false if there are no more entries. You must check // the result of Err afterwards. // // See Map.Get for further caveats around valueOut. func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool { if mi.err != nil || mi.done { return false } // For array-like maps NextKey returns nil only after maxEntries // iterations. for mi.count <= mi.maxEntries { if mi.cursor == nil { // Pass nil interface to NextKey to make sure the Map's first key // is returned. If we pass an uninitialized []byte instead, it'll see a // non-nil interface and try to marshal it. mi.cursor = make([]byte, mi.target.keySize) mi.err = mi.target.NextKey(nil, mi.cursor) } else { mi.err = mi.target.NextKey(mi.cursor, mi.cursor) } if errors.Is(mi.err, ErrKeyNotExist) { mi.done = true mi.err = nil return false } else if mi.err != nil { mi.err = fmt.Errorf("get next key: %w", mi.err) return false } mi.count++ mi.err = mi.target.Lookup(mi.cursor, valueOut) if errors.Is(mi.err, ErrKeyNotExist) { // Even though the key should be valid, we couldn't look up // its value. If we're iterating a hash map this is probably // because a concurrent delete removed the value before we // could get it. This means that the next call to NextKeyBytes // is very likely to restart iteration. // If we're iterating one of the fd maps like // ProgramArray it means that a given slot doesn't have // a valid fd associated. It's OK to continue to the next slot. continue } if mi.err != nil { mi.err = fmt.Errorf("look up next key: %w", mi.err) return false } buf := mi.cursor.([]byte) if ptr, ok := keyOut.(unsafe.Pointer); ok { copy(unsafe.Slice((*byte)(ptr), len(buf)), buf) } else { mi.err = sysenc.Unmarshal(keyOut, buf) } return mi.err == nil } mi.err = fmt.Errorf("%w", ErrIterationAborted) return false } // Err returns any encountered error. // // The method must be called after Next returns nil. // // Returns ErrIterationAborted if it wasn't possible to do a full iteration. func (mi *MapIterator) Err() error { return mi.err } // MapGetNextID returns the ID of the next eBPF map. // // Returns ErrNotExist, if there is no next eBPF map. func MapGetNextID(startID MapID) (MapID, error) { attr := &sys.MapGetNextIdAttr{Id: uint32(startID)} return MapID(attr.NextId), sys.MapGetNextId(attr) } // NewMapFromID returns the [Map] for a given map id. Returns [ErrNotExist] if // there is no eBPF map with the given id. // // Requires at least Linux 4.13. func NewMapFromID(id MapID) (*Map, error) { fd, err := sys.MapGetFdById(&sys.MapGetFdByIdAttr{ Id: uint32(id), }) if err != nil { return nil, err } return newMapFromFD(fd) } // sliceLen returns the length if the value is a slice or an error otherwise. func sliceLen(slice any) (int, error) { sliceValue := reflect.ValueOf(slice) if sliceValue.Kind() != reflect.Slice { return 0, fmt.Errorf("%T is not a slice", slice) } return sliceValue.Len(), nil } golang-github-cilium-ebpf-0.21.0+ds1/map_test.go000066400000000000000000001466731520243672000213630ustar00rootroot00000000000000package ebpf import ( "bytes" "errors" "fmt" "math" "os" "path/filepath" "runtime" "slices" "sort" "testing" "unsafe" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) var ( spec1 = &MapSpec{ Name: "foo", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } ) func TestMap(t *testing.T) { m := createMap(t, Array, 2) t.Log(m) if err := m.Put(uint32(0), uint32(42)); err != nil { t.Fatal("Can't put:", err) } if err := m.Put(uint32(1), uint32(4242)); err != nil { t.Fatal("Can't put:", err) } m2, err := m.Clone() if err != nil { t.Fatal("Can't clone map:", err) } defer m2.Close() m.Close() m = m2 var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } sliceVal := make([]uint32, 1) qt.Assert(t, qt.IsNil(m.Lookup(uint32(0), sliceVal))) qt.Assert(t, qt.DeepEquals(sliceVal, []uint32{42})) var slice []byte qt.Assert(t, qt.IsNil(m.Lookup(uint32(0), &slice))) qt.Assert(t, qt.DeepEquals(slice, internal.NativeEndian.AppendUint32(nil, 42))) var k uint32 if err := m.NextKey(uint32(0), &k); err != nil { t.Fatal("Can't get:", err) } if k != 1 { t.Error("Want key 1, got", k) } } func TestMapSpecCopy(t *testing.T) { a := &MapSpec{ "foo", Hash, 4, 4, 1, 1, PinByName, 1, []MapKV{{1, 2}}, // Can't copy Contents, use value types nil, // InnerMap 0, // MapExtra bytes.NewReader(nil), &btf.Int{}, &btf.Int{}, nil, } a.InnerMap = a qt.Check(t, qt.IsNil((*MapSpec)(nil).Copy())) qt.Assert(t, testutils.IsDeepCopy(a.Copy(), a)) } func TestMapBatch(t *testing.T) { contents := []uint32{ 42, 4242, 23, 2323, } keysAndValuesForMap := func(m *Map, contents []uint32) (keys, values []uint32, stride int) { possibleCPU := 1 if m.Type().hasPerCPUValue() { possibleCPU = MustPossibleCPU() } keys = make([]uint32, 0, len(contents)) values = make([]uint32, 0, len(contents)*possibleCPU) for key, value := range contents { keys = append(keys, uint32(key)) for i := 0; i < possibleCPU; i++ { values = append(values, value*uint32((i+1))) } } return keys, values, possibleCPU } for _, typ := range []MapType{Array, PerCPUArray} { t.Run(typ.String(), func(t *testing.T) { if typ == PerCPUArray { // https://lore.kernel.org/bpf/20210424214510.806627-2-pctammela@mojatatu.com/ testutils.SkipOnOldKernel(t, "5.13", "batched ops support for percpu array") } m := createMap(t, typ, uint32(len(contents))) keys, values, _ := keysAndValuesForMap(m, contents) count, err := m.BatchUpdate(keys, values, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(count, len(contents))) lookupKeys := make([]uint32, len(keys)) lookupValues := make([]uint32, len(values)) var cursor MapBatchCursor count, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, nil) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(count, len(contents))) qt.Assert(t, qt.ContentEquals(lookupKeys, keys)) qt.Assert(t, qt.ContentEquals(lookupValues, values)) count, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, nil) qt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist)) qt.Assert(t, qt.Equals(count, 0)) }) } for _, typ := range []MapType{Hash, PerCPUHash} { t.Run(typ.String(), func(t *testing.T) { m := createMap(t, typ, uint32(len(contents))) keys, values, stride := keysAndValuesForMap(m, contents) count, err := m.BatchUpdate(keys, values, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(count, len(contents))) // BPF hash tables seem to have lots of collisions when keys // are following a sequence. // This causes ENOSPC since a single large bucket may be larger // than the batch size. We work around this by making the batch size // equal to the map size. lookupKeys := make([]uint32, len(keys)) lookupValues := make([]uint32, len(values)) var cursor MapBatchCursor count, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, nil) qt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist)) qt.Assert(t, qt.Equals(count, len(contents))) qt.Assert(t, qt.ContentEquals(lookupKeys, keys)) qt.Assert(t, qt.ContentEquals(lookupValues, values)) cursor = MapBatchCursor{} count, err = m.BatchLookupAndDelete(&cursor, lookupKeys, lookupValues, nil) qt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist)) qt.Assert(t, qt.Equals(count, len(contents))) qt.Assert(t, qt.ContentEquals(lookupKeys, keys)) qt.Assert(t, qt.ContentEquals(lookupValues, values)) if stride > 1 { values := make([]uint32, stride) qt.Assert(t, qt.ErrorIs(m.Lookup(uint32(0), values), ErrKeyNotExist)) } else { var v uint32 qt.Assert(t, qt.ErrorIs(m.Lookup(uint32(0), &v), ErrKeyNotExist)) } }) } } func TestMapBatchCursorReuse(t *testing.T) { arr1 := createMap(t, Array, 4) arr2 := createMap(t, Array, 4) tmp := make([]uint32, 2) var cursor MapBatchCursor _, err := arr1.BatchLookup(&cursor, tmp, tmp, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) _, err = arr2.BatchLookup(&cursor, tmp, tmp, nil) qt.Assert(t, qt.IsNotNil(err)) } func TestMapLookupKeyTooSmall(t *testing.T) { m := createMap(t, Array, 2) defer m.Close() var small uint16 qt.Assert(t, qt.IsNil(m.Put(uint32(0), uint32(1234)))) qt.Assert(t, qt.IsNotNil(m.Lookup(uint32(0), &small))) } func TestMapLookupKeyNotFoundAllocations(t *testing.T) { m := createMap(t, Array, 2) defer m.Close() var key, out uint32 = 3, 0 var err error allocs := testing.AllocsPerRun(5, func() { err = m.Lookup(&key, &out) }) qt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist)) qt.Assert(t, qt.Equals(allocs, float64(0))) } func TestBatchAPIMapDelete(t *testing.T) { if err := haveBatchAPI(); err != nil { t.Skipf("batch api not available: %v", err) } m := createMap(t, Hash, 10) var ( keys = []uint32{0, 1} values = []uint32{42, 4242} ) count, err := m.BatchUpdate(keys, values, nil) if err != nil { t.Fatalf("BatchUpdate: %v", err) } if count != len(keys) { t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) } var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } count, err = m.BatchDelete(keys, nil) if err != nil { t.Fatalf("BatchDelete: %v", err) } if count != len(keys) { t.Fatalf("BatchDelete: expected %d deletions got %d", len(keys), count) } if err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) { t.Fatalf("Lookup should have failed with error, %v, instead error is %v", ErrKeyNotExist, err) } } func TestMapClose(t *testing.T) { m := createMap(t, Array, 2) if err := m.Close(); err != nil { t.Fatal("Can't close map:", err) } if err := m.Put(uint32(0), uint32(42)); !errors.Is(err, sys.ErrClosedFd) { t.Fatal("Put doesn't check for closed fd", err) } if _, err := m.LookupBytes(uint32(0)); !errors.Is(err, sys.ErrClosedFd) { t.Fatal("Get doesn't check for closed fd", err) } } func TestBatchMapWithLock(t *testing.T) { testutils.SkipOnOldKernel(t, "5.13", "MAP BATCH BPF_F_LOCK") spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/map_spin_lock-%s.elf")) qt.Assert(t, qt.IsNil(err)) coll := mustNewCollection(t, spec, nil) type spinLockValue struct { Cnt uint32 Padding uint32 } m, ok := coll.Maps["spin_lock_map"] if !ok { t.Fatal(err) } keys := []uint32{0, 1} values := []spinLockValue{{Cnt: 42}, {Cnt: 4242}} count, err := m.BatchUpdate(keys, values, &BatchOptions{ElemFlags: uint64(UpdateLock)}) testutils.SkipIfNotSupportedOnOS(t, err) if err != nil { t.Fatalf("BatchUpdate: %v", err) } if count != len(keys) { t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) } var cursor MapBatchCursor lookupKeys := make([]uint32, 2) lookupValues := make([]spinLockValue, 2) count, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, &BatchOptions{ElemFlags: uint64(LookupLock)}) if !errors.Is(err, ErrKeyNotExist) { t.Fatalf("BatchLookup: %v", err) } if count != 2 { t.Fatalf("BatchLookup: expected two keys, got %d", count) } cursor = MapBatchCursor{} deleteKeys := []uint32{0, 1} deleteValues := make([]spinLockValue, 2) count, err = m.BatchLookupAndDelete(&cursor, deleteKeys, deleteValues, nil) if !errors.Is(err, ErrKeyNotExist) { t.Fatalf("BatchLookupAndDelete: %v", err) } if count != 2 { t.Fatalf("BatchLookupAndDelete: expected two keys, got %d", count) } } func TestMapWithLock(t *testing.T) { testutils.SkipOnOldKernel(t, "5.13", "MAP BPF_F_LOCK") spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/map_spin_lock-%s.elf")) qt.Assert(t, qt.IsNil(err)) coll := mustNewCollection(t, spec, nil) type spinLockValue struct { Cnt uint32 Padding uint32 } m, ok := coll.Maps["spin_lock_map"] if !ok { t.Fatal(err) } key := uint32(1) value := spinLockValue{Cnt: 5} err = m.Update(key, value, UpdateLock) if platform.IsWindows && errors.Is(err, unix.EINVAL) { t.Skip("Windows doesn't support UpdateLock") } if err != nil { t.Fatal(err) } value.Cnt = 0 err = m.LookupWithFlags(&key, &value, LookupLock) if err != nil { t.Fatal(err) } if value.Cnt != 5 { t.Fatalf("Want value 5, got %d", value.Cnt) } t.Run("LookupAndDelete", func(t *testing.T) { testutils.SkipOnOldKernel(t, "5.14", "LOOKUP_AND_DELETE flags") value.Cnt = 0 err = m.LookupAndDeleteWithFlags(&key, &value, LookupLock) if err != nil { t.Fatal(err) } if value.Cnt != 5 { t.Fatalf("Want value 5, got %d", value.Cnt) } err = m.LookupWithFlags(&key, &value, LookupLock) if err != nil && !errors.Is(err, ErrKeyNotExist) { t.Fatal(err) } }) } func TestMapCloneNil(t *testing.T) { m, err := (*Map)(nil).Clone() if err != nil { t.Fatal(err) } if m != nil { t.Fatal("Cloning a nil map doesn't return nil") } } func TestMapPin(t *testing.T) { m := createMap(t, Array, 2) if err := m.Put(uint32(0), uint32(42)); err != nil { t.Fatal("Can't put:", err) } tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "map") if err := m.Pin(path); err != nil { testutils.SkipIfNotSupported(t, err) t.Fatal(err) } pinned := m.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) m.Close() m, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer m.Close() var v uint32 if err := m.Lookup(uint32(0), &v); err != nil { t.Fatal("Can't lookup 0:", err) } if v != 42 { t.Error("Want value 42, got", v) } } func TestNestedMapPin(t *testing.T) { m := createMapInMap(t, ArrayOfMaps, Array) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "nested") if err := m.Pin(path); err != nil { t.Fatal(err) } m.Close() m, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer m.Close() } func TestNestedMapPinNested(t *testing.T) { if _, err := newMap(t, &MapSpec{ Type: ArrayOfMaps, KeySize: 4, ValueSize: 4, MaxEntries: 2, InnerMap: &MapSpec{ Name: "inner", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, }, }, nil); err == nil { t.Error("Inner maps should not be pinnable") } } func TestMapPinMultiple(t *testing.T) { testutils.SkipOnOldKernel(t, "4.9", "atomic re-pinning was introduced in 4.9 series") tmp := testutils.TempBPFFS(t) spec := spec1.Copy() m1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp}) pinned := m1.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) newPath := filepath.Join(tmp, "bar") err := m1.Pin(newPath) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) oldPath := filepath.Join(tmp, spec.Name) if _, err := os.Stat(oldPath); err == nil { t.Fatal("Previous pinned map path still exists:", err) } m2, err := LoadPinnedMap(newPath, nil) qt.Assert(t, qt.IsNil(err)) pinned = m2.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) defer m2.Close() } func TestMapPinWithEmptyPath(t *testing.T) { m := createMap(t, Array, 2) err := m.Pin("") qt.Assert(t, qt.Not(qt.IsNil(err))) } func TestMapPinFailReplace(t *testing.T) { tmp := testutils.TempBPFFS(t) spec := spec1.Copy() spec2 := spec1.Copy() spec2.Name = spec1.Name + "bar" m := mustNewMap(t, spec, &MapOptions{PinPath: tmp}) _ = mustNewMap(t, spec2, &MapOptions{PinPath: tmp}) qt.Assert(t, qt.IsTrue(m.IsPinned())) newPath := filepath.Join(tmp, spec2.Name) qt.Assert(t, qt.Not(qt.IsNil(m.Pin(newPath))), qt.Commentf("Pin didn't"+ " fail new path from replacing an existing path")) } func TestMapUnpin(t *testing.T) { tmp := testutils.TempBPFFS(t) spec := spec1.Copy() m := mustNewMap(t, spec, &MapOptions{PinPath: tmp}) pinned := m.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) defer m2.Close() if err = m.Unpin(); err != nil { t.Fatal("Failed to unpin map:", err) } if _, err := os.Stat(path); err == nil { t.Fatal("Pinned map path still exists after unpinning:", err) } } func TestMapLoadPinned(t *testing.T) { tmp := testutils.TempBPFFS(t) spec := spec1.Copy() m1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp}) pinned := m1.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) defer m2.Close() pinned = m2.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) } func TestMapLoadReusePinned(t *testing.T) { for _, typ := range []MapType{Array, Hash, DevMap, DevMapHash} { t.Run(typ.String(), func(t *testing.T) { if typ == DevMap { testutils.SkipOnOldKernel(t, "4.14", "devmap") } if typ == DevMapHash { testutils.SkipOnOldKernel(t, "5.4", "devmap_hash") } tmp := testutils.TempBPFFS(t) spec := &MapSpec{ Name: "pinmap", Type: typ, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } _ = mustNewMap(t, spec, &MapOptions{PinPath: tmp}) _ = mustNewMap(t, spec, &MapOptions{PinPath: tmp}) }) } } func TestMapLoadPinnedUnpin(t *testing.T) { tmp := testutils.TempBPFFS(t) spec := spec1.Copy() m1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp}) pinned := m1.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) path := filepath.Join(tmp, spec.Name) m2, err := LoadPinnedMap(path, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) defer m2.Close() err = m1.Unpin() qt.Assert(t, qt.IsNil(err)) err = m2.Unpin() qt.Assert(t, qt.IsNil(err)) } func TestMapLoadPinnedWithOptions(t *testing.T) { // Introduced in commit 6e71b04a8224. testutils.SkipOnOldKernel(t, "4.15", "file_flags in BPF_OBJ_GET") array := createMap(t, Array, 2) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "map") if err := array.Pin(path); err != nil { t.Fatal(err) } if err := array.Put(uint32(0), uint32(123)); err != nil { t.Fatal(err) } array.Close() t.Run("read-only", func(t *testing.T) { array, err := LoadPinnedMap(path, &LoadPinOptions{ ReadOnly: true, }) if platform.IsWindows && errors.Is(err, unix.EINVAL) { t.Skip("Windows doesn't support file_flags in OBJ_GET") } testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't load map:", err) } defer array.Close() if err := array.Put(uint32(0), uint32(1)); !errors.Is(err, unix.EPERM) { t.Fatal("Expected EPERM from Put, got", err) } }) t.Run("write-only", func(t *testing.T) { array, err := LoadPinnedMap(path, &LoadPinOptions{ WriteOnly: true, }) if platform.IsWindows && errors.Is(err, unix.EINVAL) { t.Skip("Windows doesn't support file_flags in OBJ_GET") } testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't load map:", err) } defer array.Close() var value uint32 if err := array.Lookup(uint32(0), &value); !errors.Is(err, unix.EPERM) { t.Fatal("Expected EPERM from Lookup, got", err) } }) } func TestMapPinFlags(t *testing.T) { tmp := testutils.TempBPFFS(t) spec := &MapSpec{ Name: "map", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } _ = mustNewMap(t, spec, &MapOptions{PinPath: tmp}) _, err := newMap(t, spec, &MapOptions{ PinPath: tmp, LoadPinOptions: LoadPinOptions{ Flags: math.MaxUint32, }, }) if !errors.Is(err, unix.EINVAL) { t.Fatal("Invalid flags should trigger EINVAL:", err) } } func TestMapQueue(t *testing.T) { testutils.SkipOnOldKernel(t, "4.20", "map type queue") m := mustNewMap(t, &MapSpec{ Type: Queue, ValueSize: 4, MaxEntries: 2, }, nil) for _, v := range []uint32{42, 4242} { if err := m.Put(nil, v); err != nil { t.Fatalf("Can't put %d: %s", v, err) } } var v uint32 if err := m.Lookup(nil, &v); err != nil { t.Fatal("Lookup (Peek) on Queue:", err) } if v != 42 { t.Error("Want value 42, got", v) } v = 0 if err := m.LookupAndDelete(nil, &v); err != nil { t.Fatal("Can't lookup and delete element:", err) } if v != 42 { t.Error("Want value 42, got", v) } v = 0 if err := m.LookupAndDelete(nil, unsafe.Pointer(&v)); err != nil { t.Fatal("Can't lookup and delete element using unsafe.Pointer:", err) } if v != 4242 { t.Error("Want value 4242, got", v) } if err := m.LookupAndDelete(nil, &v); !errors.Is(err, ErrKeyNotExist) { t.Fatal("Lookup and delete on empty Queue:", err) } if err := m.Lookup(nil, &v); !errors.Is(err, ErrKeyNotExist) { t.Fatal("Lookup (Peek) on empty Queue:", err) } } func TestMapInMap(t *testing.T) { for _, typ := range []MapType{ArrayOfMaps, HashOfMaps} { t.Run(typ.String(), func(t *testing.T) { inner := createMap(t, Array, 2) if err := inner.Put(uint32(1), uint32(4242)); err != nil { t.Fatal(err) } outer := createMapInMap(t, typ, Array) if err := outer.Put(uint32(0), inner); err != nil { t.Fatal("Can't put inner map:", err) } if err := outer.Put(uint32(0), (*Map)(nil)); err == nil { t.Fatal("Put accepted a nil Map") } var inner2 *Map if err := outer.Lookup(uint32(0), &inner2); err != nil { t.Fatal("Can't lookup 0:", err) } defer inner2.Close() var v uint32 if err := inner2.Lookup(uint32(1), &v); err != nil { t.Fatal("Can't lookup 1 in inner2:", err) } if v != 4242 { t.Error("Expected value 4242, got", v) } inner2.Close() // Make sure we can still access the original map if err := inner.Lookup(uint32(1), &v); err != nil { t.Fatal("Can't lookup 1 in inner:", err) } if v != 4242 { t.Error("Expected value 4242, got", v) } }) } } func TestNewMapInMapFromFD(t *testing.T) { nested := createMapInMap(t, ArrayOfMaps, Array) // Do not copy this, use Clone instead. another, err := NewMapFromFD(testutils.DupFD(t, nested.FD())) testutils.SkipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) another.Close() } func TestPerfEventArray(t *testing.T) { specs := []*MapSpec{ {Type: PerfEventArray}, {Type: PerfEventArray, KeySize: 4}, {Type: PerfEventArray, ValueSize: 4}, } for _, spec := range specs { _ = mustNewMap(t, spec, nil) } } func TestCPUMap(t *testing.T) { testutils.SkipOnOldKernel(t, "4.15", "cpu map") m := mustNewMap(t, &MapSpec{Type: CPUMap, KeySize: 4, ValueSize: 4}, nil) qt.Assert(t, qt.Equals(m.MaxEntries(), uint32(MustPossibleCPU()))) } func TestMapInMapValueSize(t *testing.T) { spec := &MapSpec{ Type: ArrayOfMaps, KeySize: 4, ValueSize: 0, MaxEntries: 2, InnerMap: &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, }, } _ = mustNewMap(t, spec, nil) spec.ValueSize = 4 _ = mustNewMap(t, spec, nil) spec.ValueSize = 1 _, err := newMap(t, spec, nil) qt.Assert(t, qt.IsNotNil(err)) } func TestIterateEmptyMap(t *testing.T) { makeMap := func(t *testing.T, mapType MapType) *Map { m, err := newMap(t, &MapSpec{ Type: mapType, KeySize: 4, ValueSize: 8, MaxEntries: 2, }, nil) if errors.Is(err, unix.EINVAL) { t.Skip(mapType, "is not supported") } qt.Assert(t, qt.IsNil(err)) return m } for _, mapType := range []MapType{ Hash, SockHash, } { t.Run(mapType.String(), func(t *testing.T) { m := makeMap(t, mapType) entries := m.Iterate() var key string var value uint64 if entries.Next(&key, &value) { t.Error("Empty hash should not be iterable") } if err := entries.Err(); err != nil { t.Error("Empty hash shouldn't return an error:", err) } }) } for _, mapType := range []MapType{ Array, SockMap, } { t.Run(mapType.String(), func(t *testing.T) { m := makeMap(t, mapType) entries := m.Iterate() var key string var value uint64 for entries.Next(&key, &value) { // Some empty arrays like sockmap don't return any keys. } if err := entries.Err(); err != nil { t.Error("Empty array shouldn't return an error:", err) } }) } } func TestMapIterate(t *testing.T) { hash := createMap(t, Hash, 2) data := []string{"test", "more"} slices.Sort(data) for i, k := range data { if err := hash.Put(k, uint32(i)); err != nil { t.Fatal(err) } } var key string var value uint32 var keys []string entries := hash.Iterate() for entries.Next(&key, &value) { keys = append(keys, key) } qt.Assert(t, qt.IsNil(entries.Err())) sort.Strings(keys) qt.Assert(t, qt.DeepEquals(keys, data)) } func TestIterateWrongMap(t *testing.T) { testutils.SkipOnOldKernel(t, "4.20", "map type queue") m := mustNewMap(t, &MapSpec{ Type: Queue, ValueSize: 4, MaxEntries: 2, Contents: []MapKV{ {nil, uint32(0)}, {nil, uint32(1)}, }, }, nil) var value uint32 entries := m.Iterate() qt.Assert(t, qt.IsFalse(entries.Next(nil, &value))) qt.Assert(t, qt.IsNotNil(entries.Err())) } func TestMapIteratorAllocations(t *testing.T) { arr := createMap(t, Array, 10) var k, v uint32 iter := arr.Iterate() // AllocsPerRun warms up the function for us. allocs := testing.AllocsPerRun(int(arr.MaxEntries()-1), func() { if !iter.Next(&k, &v) { t.Fatal("Next failed while iterating: %w", iter.Err()) } }) qt.Assert(t, qt.Equals(allocs, float64(0))) } func TestMapBatchLookupAllocations(t *testing.T) { testutils.SkipIfNotSupported(t, haveBatchAPI()) for _, typ := range []MapType{Array, PerCPUArray} { if typ == PerCPUArray { // https://lore.kernel.org/bpf/20210424214510.806627-2-pctammela@mojatatu.com/ testutils.SkipOnOldKernel(t, "5.13", "batched ops support for percpu array") } t.Run(typ.String(), func(t *testing.T) { m := mustNewMap(t, &MapSpec{ Name: "test", Type: typ, KeySize: 4, ValueSize: 8, // PerCPU values must be 8 byte aligned. MaxEntries: 10, }, nil) possibleCPU := 1 if m.Type().hasPerCPUValue() { possibleCPU = MustPossibleCPU() } var cursor MapBatchCursor keys := any(make([]uint32, 2)) values := any(make([]uint64, 2*possibleCPU)) // AllocsPerRun warms up the function for us. allocs := testing.AllocsPerRun(1, func() { _, err := m.BatchLookup(&cursor, keys, values, nil) if err != nil { t.Fatal(err) } }) qt.Assert(t, qt.Equals(allocs, 0)) }) } } type customTestUnmarshaler []uint8 func (c customTestUnmarshaler) UnmarshalBinary(data []byte) error { chunkSize := len(data) / len(c) for i := range len(data) / chunkSize { c[i] = data[i*chunkSize] } return nil } func TestMapBatchLookupCustomUnmarshaler(t *testing.T) { testutils.SkipIfNotSupported(t, haveBatchAPI()) m := mustNewMap(t, &MapSpec{ Type: Array, MaxEntries: 3, KeySize: 4, ValueSize: 4, Contents: []MapKV{ {uint32(0), uint32(3)}, {uint32(1), uint32(4)}, {uint32(2), uint32(5)}, }, }, nil) var ( cursor MapBatchCursor // Use data structures that result in different memory size than the // map keys and values. Otherwise their memory is used as backing // memory for the syscall directly and Unmarshal is a no-op. // Use batch size that results in partial second lookup. batchKeys = make(customTestUnmarshaler, 2) batchValues = make(customTestUnmarshaler, 2) keys []uint8 values []uint8 ) _, err := m.BatchLookup(&cursor, batchKeys, batchValues, nil) if err != nil { t.Fatal("Full batch lookup failed:", err) } keys = append(keys, batchKeys...) values = append(values, batchValues...) _, err = m.BatchLookup(&cursor, batchKeys, batchValues, nil) if !errors.Is(err, ErrKeyNotExist) { t.Fatal("Partial batch lookup doesn't return ErrKeyNotExist:", err) } keys = append(keys, batchKeys[0]) values = append(values, batchValues[0]) qt.Assert(t, qt.DeepEquals(keys, []uint8{0, 1, 2})) qt.Assert(t, qt.DeepEquals(values, []uint8{3, 4, 5})) } func TestMapIterateHashKeyOneByteFull(t *testing.T) { hash := mustNewMap(t, &MapSpec{ Type: Hash, KeySize: 1, ValueSize: 1, MaxEntries: 256, }, nil) for i := 0; i < int(hash.MaxEntries()); i++ { if err := hash.Put(uint8(i), uint8(i)); err != nil { t.Fatal(err) } } var key uint8 var value uint8 var keys int entries := hash.Iterate() for entries.Next(&key, &value) { if key != value { t.Fatalf("Expected key == value, got key %v value %v", key, value) } keys++ } if err := entries.Err(); err != nil { t.Fatal(err) } if keys != int(hash.MaxEntries()) { t.Fatalf("Expected to get %d keys, have %d", hash.MaxEntries(), keys) } } func TestMapGuessNonExistentKey(t *testing.T) { if !platform.IsLinux { t.Skip("No need to test linux quirk on", runtime.GOOS) } tests := []struct { name string mapType MapType keys []uint32 }{ { "empty", Hash, []uint32{}, }, { "all zero key", Hash, []uint32{0}, }, { "all ones key", Hash, []uint32{math.MaxUint32}, }, { "alternating bits key", Hash, []uint32{0x5555_5555}, }, { "all special patterns", Hash, []uint32{0, math.MaxUint32, 0x5555_5555}, }, { "empty", Array, []uint32{}, }, { "all zero key", Array, []uint32{0}, }, { "full", Array, []uint32{0, 1}, }, } for _, tt := range tests { t.Run(fmt.Sprintf("%s: %s", tt.mapType, tt.name), func(t *testing.T) { maxEntries := uint32(len(tt.keys)) if maxEntries == 0 { maxEntries = 1 } m := mustNewMap(t, &MapSpec{ Type: tt.mapType, KeySize: 4, ValueSize: 4, MaxEntries: maxEntries, }, nil) for _, key := range tt.keys { if err := m.Put(key, key); err != nil { t.Fatal(err) } } guess, err := m.guessNonExistentKey() if err != nil { t.Fatal(err) } if len(guess) != int(m.keySize) { t.Fatal("Guessed key has wrong size") } var value uint32 if err := m.Lookup(guess, &value); !errors.Is(err, unix.ENOENT) { t.Fatal("Doesn't return ENOENT:", err) } }) } t.Run("Hash: full", func(t *testing.T) { const n = math.MaxUint8 + 1 hash := mustNewMap(t, &MapSpec{ Type: Hash, KeySize: 1, ValueSize: 1, MaxEntries: n, }, nil) for i := 0; i < n; i++ { if err := hash.Put(uint8(i), uint8(i)); err != nil { t.Fatal(err) } } _, err := hash.guessNonExistentKey() if err == nil { t.Fatal("guessNonExistentKey doesn't return error on full hash table") } }) } func TestNotExist(t *testing.T) { hash := createMap(t, Hash, 10) var tmp uint32 err := hash.Lookup("test", &tmp) if !errors.Is(err, ErrKeyNotExist) { t.Error("Lookup doesn't return ErrKeyNotExist") } buf, err := hash.LookupBytes("test") if err != nil { t.Error("Looking up non-existent key return an error:", err) } if buf != nil { t.Error("LookupBytes returns non-nil buffer for non-existent key") } if err := hash.Delete("test"); !errors.Is(err, ErrKeyNotExist) { t.Error("Deleting unknown key doesn't return ErrKeyNotExist", err) } var k = []byte{1, 2, 3, 4} if err := hash.NextKey(&k, &tmp); !errors.Is(err, ErrKeyNotExist) { t.Error("Looking up next key in empty map doesn't return a non-existing error", err) } if err := hash.NextKey(nil, &tmp); !errors.Is(err, ErrKeyNotExist) { t.Error("Looking up next key in empty map doesn't return a non-existing error", err) } } func TestExist(t *testing.T) { hash := createMap(t, Hash, 10) if err := hash.Put("test", uint32(21)); err != nil { t.Errorf("Failed to put key/value pair into hash: %v", err) } if err := hash.Update("test", uint32(42), UpdateNoExist); !errors.Is(err, ErrKeyExist) { t.Error("Updating existing key doesn't return ErrKeyExist") } } func TestIterateMapInMap(t *testing.T) { const idx = uint32(1) parent := createMapInMap(t, ArrayOfMaps, Array) defer parent.Close() a := createMap(t, Array, 2) if err := parent.Put(idx, a); err != nil { t.Fatal(err) } var ( key uint32 m *Map entries = parent.Iterate() ) if !entries.Next(&key, &m) { t.Fatal("Iterator encountered error:", entries.Err()) } m.Close() if key != 1 { t.Error("Iterator didn't skip first entry") } if m == nil { t.Fatal("Map is nil") } } func TestPerCPUMarshaling(t *testing.T) { for _, typ := range []MapType{PerCPUHash, PerCPUArray, LRUCPUHash} { t.Run(typ.String(), func(t *testing.T) { numCPU := MustPossibleCPU() if numCPU < 2 { t.Skip("Test requires at least two CPUs") } if typ == PerCPUHash || typ == PerCPUArray { testutils.SkipOnOldKernel(t, "4.6", "per-CPU hash and array") } if typ == LRUCPUHash { testutils.SkipOnOldKernel(t, "4.10", "LRU per-CPU hash") } arr := createMap(t, typ, 1) values := []*customEncoding{ {"test"}, {"more"}, } if err := arr.Put(uint32(0), values); err != nil { t.Fatal(err) } // Make sure unmarshaling works on slices containing pointers retrievedVal := make([]*customEncoding, numCPU) if err := arr.Lookup(uint32(0), retrievedVal); err == nil { t.Fatal("Slices with nil values should generate error") } for i := range retrievedVal { retrievedVal[i] = &customEncoding{} } if err := arr.Lookup(uint32(0), retrievedVal); err != nil { t.Fatal("Can't retrieve key 0:", err) } var retrieved []*customEncoding if err := arr.Lookup(uint32(0), &retrieved); err != nil { t.Fatal("Can't retrieve key 0:", err) } for i, want := range []string{"TEST", "MORE"} { if retrieved[i] == nil { t.Error("First item is nil") } else if have := retrieved[i].data; have != want { t.Errorf("Put doesn't use BinaryMarshaler, expected %s but got %s", want, have) } } }) } } type bpfCgroupStorageKey struct { CgroupInodeId uint64 AttachType AttachType _ [4]byte // Padding } func TestCgroupPerCPUStorageMarshaling(t *testing.T) { numCPU := MustPossibleCPU() if numCPU < 2 { t.Skip("Test requires at least two CPUs") } testutils.SkipOnOldKernel(t, "5.9", "per-CPU CGoup storage with write from user space support") arr := mustNewMap(t, &MapSpec{ Type: PerCPUCGroupStorage, KeySize: uint32(unsafe.Sizeof(bpfCgroupStorageKey{})), ValueSize: uint32(unsafe.Sizeof(uint64(0))), }, nil) prog := mustNewProgram(t, &ProgramSpec{ Type: CGroupSKB, AttachType: AttachCGroupInetEgress, License: "MIT", Instructions: asm.Instructions{ asm.LoadMapPtr(asm.R1, arr.FD()), asm.Mov.Imm(asm.R2, 0), asm.FnGetLocalStorage.Call(), asm.Mov.Imm(asm.R0, 0), asm.Return(), }, }, nil) cgroup := testutils.CreateCgroup(t) progAttachAttrs := sys.ProgAttachAttr{ TargetFdOrIfindex: uint32(cgroup.Fd()), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(AttachCGroupInetEgress), AttachFlags: 0, ReplaceBpfFd: 0, } err := sys.ProgAttach(&progAttachAttrs) if err != nil { t.Fatal(err) } defer func() { attr := sys.ProgDetachAttr{ TargetFdOrIfindex: uint32(cgroup.Fd()), AttachBpfFd: uint32(prog.FD()), AttachType: uint32(AttachCGroupInetEgress), } if err := sys.ProgDetach(&attr); err != nil { t.Fatal(err) } }() var mapKey = &bpfCgroupStorageKey{ CgroupInodeId: testutils.GetCgroupIno(t, cgroup), AttachType: AttachCGroupInetEgress, } values := []uint64{1, 2} if err := arr.Put(mapKey, values); err != nil { t.Fatalf("Can't set cgroup %s storage: %s", cgroup.Name(), err) } var retrieved []uint64 if err := arr.Lookup(mapKey, &retrieved); err != nil { t.Fatalf("Can't retrieve cgroup %s storage: %s", cgroup.Name(), err) } for i, want := range []uint64{1, 2} { if retrieved[i] == 0 { t.Errorf("Item %d is 0", i) } else if have := retrieved[i]; have != want { t.Errorf("PerCPUCGroupStorage map is not correctly unmarshaled, expected %d but got %d", want, have) } } } func TestMapMarshalUnsafe(t *testing.T) { m := createMap(t, Hash, 1) key := uint32(1) value := uint32(42) if err := m.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil { t.Fatal(err) } var res uint32 if err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&res)); err != nil { t.Fatal("Can't get item:", err) } var sum uint32 iter := m.Iterate() for iter.Next(&key, unsafe.Pointer(&res)) { sum += res } if err := iter.Err(); err != nil { t.Fatal(err) } if res != 42 { t.Fatalf("Expected 42, got %d", res) } iter = m.Iterate() iter.Next(unsafe.Pointer(&key), &res) if err := iter.Err(); err != nil { t.Error(err) } if key != 1 { t.Errorf("Expected key 1, got %d", key) } if err := m.Delete(unsafe.Pointer(&key)); err != nil { t.Fatal("Can't delete:", err) } } func TestMapName(t *testing.T) { testutils.SkipIfNotSupported(t, haveObjName()) m := mustNewMap(t, &MapSpec{ Name: "test!123", Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1, }, nil) var info sys.MapInfo if err := sys.ObjInfo(m.fd, &info); err != nil { t.Fatal(err) } name := unix.ByteSliceToString(info.Name[:]) qt.Assert(t, qt.Equals(name, "test123")) } func TestMapFromFD(t *testing.T) { m := createMap(t, Array, 2) if err := m.Put(uint32(0), uint32(123)); err != nil { t.Fatal(err) } // If you're thinking about copying this, don't. Use // Clone() instead. m2, err := NewMapFromFD(testutils.DupFD(t, m.FD())) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer m2.Close() var val uint32 if err := m2.Lookup(uint32(0), &val); err != nil { t.Fatal("Can't look up key:", err) } if val != 123 { t.Error("Wrong value") } } func TestMapContents(t *testing.T) { spec := &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, Contents: []MapKV{ {uint32(0), uint32(23)}, {uint32(1), uint32(42)}, }, } m := mustNewMap(t, spec, nil) var value uint32 if err := m.Lookup(uint32(0), &value); err != nil { t.Error("Can't look up key 0:", err) } else if value != 23 { t.Errorf("Incorrect value for key 0, expected 23, have %d", value) } if err := m.Lookup(uint32(1), &value); err != nil { t.Error("Can't look up key 1:", err) } else if value != 42 { t.Errorf("Incorrect value for key 0, expected 23, have %d", value) } spec.Contents = []MapKV{ // Key is larger than MaxEntries {uint32(14), uint32(0)}, } // Invalid contents should be rejected _, err := newMap(t, spec, nil) qt.Assert(t, qt.IsNotNil(err)) } func TestMapFreeze(t *testing.T) { arr := createMap(t, Array, 2) err := arr.Freeze() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Can't freeze map:", err) } if err := arr.Put(uint32(0), uint32(1)); err == nil { t.Error("Freeze doesn't prevent modification from user space") } info, err := arr.Info() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(info.Frozen())) } func TestMapGetNextID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "bpf_map_get_next_id") var next MapID var err error // Ensure there is at least one map on the system. _ = createMap(t, Hash, 10) if next, err = MapGetNextID(MapID(0)); err != nil { t.Fatal("Can't get next ID:", err) } if next == MapID(0) { t.Fatal("Expected next ID other than 0") } // As there can be multiple eBPF maps, we loop over all of them and // make sure, the IDs increase and the last call will return ErrNotExist for { last := next if next, err = MapGetNextID(last); err != nil { if !errors.Is(err, os.ErrNotExist) { t.Fatal("Expected ErrNotExist, got:", err) } break } if next <= last { t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last) } } } func TestNewMapFromID(t *testing.T) { hash := createMap(t, Hash, 10) info, err := hash.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Couldn't get map info:", err) } id, ok := info.ID() if !ok { t.Skip("Map ID not supported") } hash2, err := NewMapFromID(id) if err != nil { t.Fatalf("Can't get map for ID %d: %v", id, err) } hash2.Close() // As there can be multiple maps, we use max(uint32) as MapID to trigger an expected error. _, err = NewMapFromID(MapID(math.MaxUint32)) if !errors.Is(err, os.ErrNotExist) { t.Fatal("Expected ErrNotExist, got:", err) } } func TestMapPinning(t *testing.T) { tmp := testutils.TempBPFFS(t) spec := &MapSpec{ Name: "test", Type: Hash, KeySize: 4, ValueSize: 4, MaxEntries: 1, Pinning: PinByName, } m1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp}) pinned := m1.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) m1Info, err := m1.Info() qt.Assert(t, qt.IsNil(err)) if err := m1.Put(uint32(0), uint32(42)); err != nil { t.Fatal("Can't write value:", err) } m2 := mustNewMap(t, spec, &MapOptions{PinPath: tmp}) m2Info, err := m2.Info() qt.Assert(t, qt.IsNil(err)) if m1ID, ok := m1Info.ID(); ok { m2ID, _ := m2Info.ID() qt.Assert(t, qt.Equals(m2ID, m1ID)) } var value uint32 if err := m2.Lookup(uint32(0), &value); err != nil { t.Fatal("Can't read from map:", err) } if value != 42 { t.Fatal("Pinning doesn't use pinned maps") } spec.KeySize = 8 spec.ValueSize = 8 _, err = newMap(t, spec, &MapOptions{PinPath: tmp}) if err == nil { t.Fatalf("Opening a pinned map with a mismatching spec did not fail") } if !errors.Is(err, ErrMapIncompatible) { t.Fatalf("Opening a pinned map with a mismatching spec failed with the wrong error") } // Check if error string mentions both KeySize and ValueSize. qt.Assert(t, qt.StringContains(err.Error(), "KeySize")) qt.Assert(t, qt.StringContains(err.Error(), "ValueSize")) } func TestMapHandle(t *testing.T) { kv := &btf.Int{Size: 4} m := mustNewMap(t, &MapSpec{ Type: Hash, KeySize: kv.Size, ValueSize: kv.Size, Key: kv, Value: kv, MaxEntries: 1, }, nil) h, err := m.Handle() testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNotNil(h)) defer h.Close() spec, err := h.Spec(nil) qt.Assert(t, qt.IsNil(err)) typ, err := spec.TypeByID(1) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.ContentEquals(typ, btf.Type(kv))) } func TestPerfEventArrayCompatible(t *testing.T) { ms := &MapSpec{ Type: PerfEventArray, } m := mustNewMap(t, ms, nil) qt.Assert(t, qt.IsNil(ms.Compatible(m))) ms.MaxEntries = m.MaxEntries() - 1 qt.Assert(t, qt.IsNotNil(ms.Compatible(m))) } func TestLoadWrongPin(t *testing.T) { p := createBasicProgram(t) m := createMap(t, Hash, 10) tmp := testutils.TempBPFFS(t) ppath := filepath.Join(tmp, "prog") mpath := filepath.Join(tmp, "map") qt.Assert(t, qt.IsNil(m.Pin(mpath))) qt.Assert(t, qt.IsNil(p.Pin(ppath))) t.Run("Program", func(t *testing.T) { lp, err := LoadPinnedProgram(ppath, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(lp.Close())) _, err = LoadPinnedProgram(mpath, nil) qt.Assert(t, qt.IsNotNil(err)) }) t.Run("Map", func(t *testing.T) { lm, err := LoadPinnedMap(mpath, nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(lm.Close())) _, err = LoadPinnedMap(ppath, nil) qt.Assert(t, qt.IsNotNil(err)) }) } type benchValue struct { ID uint32 Val16 uint16 Val16_2 uint16 Name [8]byte LID uint64 } type customBenchValue benchValue func (cbv *customBenchValue) UnmarshalBinary(buf []byte) error { cbv.ID = internal.NativeEndian.Uint32(buf) cbv.Val16 = internal.NativeEndian.Uint16(buf[4:]) cbv.Val16_2 = internal.NativeEndian.Uint16(buf[6:]) copy(cbv.Name[:], buf[8:]) cbv.LID = internal.NativeEndian.Uint64(buf[16:]) return nil } func (cbv *customBenchValue) MarshalBinary() ([]byte, error) { buf := make([]byte, 24) internal.NativeEndian.PutUint32(buf, cbv.ID) internal.NativeEndian.PutUint16(buf[4:], cbv.Val16) internal.NativeEndian.PutUint16(buf[6:], cbv.Val16_2) copy(buf[8:], cbv.Name[:]) internal.NativeEndian.PutUint64(buf[16:], cbv.LID) return buf, nil } type benchKey struct { id uint64 } func (bk *benchKey) MarshalBinary() ([]byte, error) { buf := make([]byte, 8) internal.NativeEndian.PutUint64(buf, bk.id) return buf, nil } func BenchmarkMarshaling(b *testing.B) { newMap := func(valueSize uint32) *Map { return mustNewMap(b, &MapSpec{ Type: Hash, KeySize: 8, ValueSize: valueSize, MaxEntries: 1, }, nil) } key := uint64(0) m := newMap(24) if err := m.Put(key, benchValue{}); err != nil { b.Fatal(err) } b.Cleanup(func() { m.Close() }) b.Run("ValueUnmarshalReflect", func(b *testing.B) { b.ReportAllocs() var value benchValue for b.Loop() { err := m.Lookup(unsafe.Pointer(&key), &value) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("KeyMarshalReflect", func(b *testing.B) { b.ReportAllocs() var value benchValue for b.Loop() { err := m.Lookup(&key, unsafe.Pointer(&value)) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("ValueBinaryUnmarshaler", func(b *testing.B) { b.ReportAllocs() var value customBenchValue for b.Loop() { err := m.Lookup(unsafe.Pointer(&key), &value) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("KeyBinaryMarshaler", func(b *testing.B) { b.ReportAllocs() var key benchKey var value customBenchValue for b.Loop() { err := m.Lookup(&key, unsafe.Pointer(&value)) if err != nil { b.Fatal("Can't get key:", err) } } }) b.Run("KeyValueUnsafe", func(b *testing.B) { b.ReportAllocs() var value benchValue for b.Loop() { err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)) if err != nil { b.Fatal("Can't get key:", err) } } }) } func BenchmarkPerCPUMarshalling(b *testing.B) { key := uint64(1) val := make([]uint64, MustPossibleCPU()) for i := range val { val[i] = uint64(i) } m := mustNewMap(b, &MapSpec{ Type: PerCPUHash, KeySize: 8, ValueSize: 8, MaxEntries: 1, }, nil) if err := m.Put(key, val[0:]); err != nil { b.Fatal(err) } b.Run("reflection", func(b *testing.B) { b.ReportAllocs() var value []uint64 for b.Loop() { err := m.Lookup(unsafe.Pointer(&key), &value) if err != nil { b.Fatal("Can't get key:", err) } } }) } func BenchmarkMap(b *testing.B) { m := createMap(b, Hash, 1) if err := m.Put(uint32(0), uint32(42)); err != nil { b.Fatal(err) } b.Run("Lookup", func(b *testing.B) { var key, value uint32 b.ReportAllocs() for b.Loop() { err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)) if err != nil { b.Fatal(err) } } }) b.Run("Update", func(b *testing.B) { var key, value uint32 b.ReportAllocs() for b.Loop() { err := m.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), UpdateAny) if err != nil { b.Fatal(err) } } }) b.Run("NextKey", func(b *testing.B) { var key uint32 b.ReportAllocs() for b.Loop() { err := m.NextKey(nil, unsafe.Pointer(&key)) if err != nil { b.Fatal(err) } } }) b.Run("Delete", func(b *testing.B) { var key uint32 b.ReportAllocs() for b.Loop() { err := m.Delete(unsafe.Pointer(&key)) if err != nil && !errors.Is(err, ErrKeyNotExist) { b.Fatal(err) } } }) } func BenchmarkIterate(b *testing.B) { for _, mt := range []MapType{Hash, PerCPUHash} { m := mustNewMap(b, &MapSpec{ Type: mt, KeySize: 8, ValueSize: 8, MaxEntries: 1000, }, nil) possibleCPU := 1 if m.Type().hasPerCPUValue() { possibleCPU = MustPossibleCPU() } var ( n = m.MaxEntries() keys = make([]uint64, n) values = make([]uint64, n*uint32(possibleCPU)) ) for i := 0; uint32(i) < n; i++ { keys[i] = uint64(i) for j := 0; j < possibleCPU; j++ { values[i] = uint64((i * possibleCPU) + j) } } _, err := m.BatchUpdate(keys, values, nil) testutils.SkipIfNotSupported(b, err) qt.Assert(b, qt.IsNil(err)) b.Run(m.Type().String(), func(b *testing.B) { b.Run("MapIterator", func(b *testing.B) { var k uint64 v := make([]uint64, possibleCPU) b.ReportAllocs() for b.Loop() { iter := m.Iterate() for iter.Next(&k, v) { continue } if err := iter.Err(); err != nil { b.Fatal(err) } } }) b.Run("MapIteratorDelete", func(b *testing.B) { var k uint64 v := make([]uint64, possibleCPU) b.ReportAllocs() for b.Loop() { b.StopTimer() if _, err := m.BatchUpdate(keys, values, nil); err != nil { b.Fatal(err) } b.StartTimer() iter := m.Iterate() for iter.Next(&k, &v) { if err := m.Delete(&k); err != nil { b.Fatal(err) } } if err := iter.Err(); err != nil { b.Fatal(err) } } }) b.Run("BatchLookup", func(b *testing.B) { k := make([]uint64, m.MaxEntries()) v := make([]uint64, m.MaxEntries()*uint32(possibleCPU)) b.ReportAllocs() for b.Loop() { var cursor MapBatchCursor for { _, err := m.BatchLookup(&cursor, k, v, nil) if errors.Is(err, ErrKeyNotExist) { break } if err != nil { b.Fatal(err) } } } }) b.Run("BatchLookupAndDelete", func(b *testing.B) { k := make([]uint64, m.MaxEntries()) v := make([]uint64, m.MaxEntries()*uint32(possibleCPU)) b.ReportAllocs() for b.Loop() { b.StopTimer() if _, err := m.BatchUpdate(keys, values, nil); err != nil { b.Fatal(err) } b.StartTimer() var cursor MapBatchCursor for { _, err := m.BatchLookupAndDelete(&cursor, k, v, nil) if errors.Is(err, ErrKeyNotExist) { break } if err != nil { b.Fatal(err) } } } }) b.Run("BatchDelete", func(b *testing.B) { b.ReportAllocs() for b.Loop() { b.StopTimer() if _, err := m.BatchUpdate(keys, values, nil); err != nil { b.Fatal(err) } b.StartTimer() if _, err := m.BatchDelete(keys, nil); err != nil { b.Fatal(err) } } }) }) } } // Per CPU maps store a distinct value for each CPU. They are useful // to collect metrics. func ExampleMap_perCPU() { arr, err := NewMap(&MapSpec{ Type: PerCPUArray, KeySize: 4, ValueSize: 4, MaxEntries: 2, }) if err != nil { panic(err) } defer arr.Close() possibleCPUs := MustPossibleCPU() perCPUValues := map[uint32]uint32{ 0: 4, 1: 5, } for k, v := range perCPUValues { // We set each perCPU slots to the same value. values := make([]uint32, possibleCPUs) for i := range values { values[i] = v } if err := arr.Put(k, values); err != nil { panic(err) } } for k := 0; k < 2; k++ { var values []uint32 if err := arr.Lookup(uint32(k), &values); err != nil { panic(err) } // Note we will print an unexpected message if this is not true. fmt.Printf("Value of key %v on all CPUs: %v\n", k, values[0]) } var ( key uint32 entries = arr.Iterate() ) var values []uint32 for entries.Next(&key, &values) { expected, ok := perCPUValues[key] if !ok { fmt.Printf("Unexpected key %v\n", key) continue } for i, n := range values { if n != expected { fmt.Printf("Key %v, Value for cpu %v is %v not %v\n", key, i, n, expected) } } } if err := entries.Err(); err != nil { panic(err) } } // It is possible to use unsafe.Pointer to avoid marshalling // and copy overhead. It is the responsibility of the caller to ensure // the correct size of unsafe.Pointers. // // Note that using unsafe.Pointer is only marginally faster than // implementing Marshaler on the type. func ExampleMap_zeroCopy() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { panic(err) } defer hash.Close() key := [5]byte{'h', 'e', 'l', 'l', 'o'} value := uint32(23) if err := hash.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil { panic(err) } value = 0 if err := hash.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil { panic("can't get value:" + err.Error()) } fmt.Printf("The value is: %d\n", value) } func ExampleMap_NextKey() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, Contents: []MapKV{ {"hello", uint32(21)}, {"world", uint32(42)}, }, }) if err != nil { panic(err) } defer hash.Close() var cur, next string var keys []string for err = hash.NextKey(nil, &next); ; err = hash.NextKey(cur, &next) { if errors.Is(err, ErrKeyNotExist) { break } if err != nil { panic(err) } keys = append(keys, next) cur = next } // Order of keys is non-deterministic due to randomized map seed sort.Strings(keys) fmt.Printf("Keys are %v\n", keys) } // ExampleMap_Iterate demonstrates how to iterate over all entries // in a map. func ExampleMap_Iterate() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, Contents: []MapKV{ {"hello", uint32(21)}, {"world", uint32(42)}, }, }) if err != nil { panic(err) } defer hash.Close() var ( key string value uint32 entries = hash.Iterate() ) values := make(map[string]uint32) for entries.Next(&key, &value) { // Order of keys is non-deterministic due to randomized map seed values[key] = value } if err := entries.Err(); err != nil { panic(fmt.Sprint("Iterator encountered an error:", err)) } for k, v := range values { fmt.Printf("key: %s, value: %d\n", k, v) } } // It is possible to iterate nested maps and program arrays by // unmarshaling into a *Map or *Program. func ExampleMap_Iterate_nestedMapsAndProgramArrays() { inner := &MapSpec{ Type: Array, KeySize: 4, ValueSize: 4, MaxEntries: 2, Contents: []MapKV{ {uint32(0), uint32(1)}, {uint32(1), uint32(2)}, }, } im, err := NewMap(inner) if err != nil { panic(err) } defer im.Close() outer := &MapSpec{ Type: ArrayOfMaps, InnerMap: inner, KeySize: 4, ValueSize: 4, MaxEntries: 10, Contents: []MapKV{ {uint32(0), im}, }, } arrayOfMaps, err := NewMap(outer) if errors.Is(err, internal.ErrNotSupported) { // Fake the output if on very old kernel. fmt.Println("outerKey: 0") fmt.Println("\tinnerKey 0 innerValue 1") fmt.Println("\tinnerKey 1 innerValue 2") return } if err != nil { panic(err) } defer arrayOfMaps.Close() var ( key uint32 m *Map entries = arrayOfMaps.Iterate() ) for entries.Next(&key, &m) { // Make sure that the iterated map is closed after // we are done. defer m.Close() // Order of keys is non-deterministic due to randomized map seed fmt.Printf("outerKey: %v\n", key) var innerKey, innerValue uint32 items := m.Iterate() for items.Next(&innerKey, &innerValue) { fmt.Printf("\tinnerKey %v innerValue %v\n", innerKey, innerValue) } if err := items.Err(); err != nil { panic(fmt.Sprint("Inner Iterator encountered an error:", err)) } } if err := entries.Err(); err != nil { panic(fmt.Sprint("Iterator encountered an error:", err)) } } golang-github-cilium-ebpf-0.21.0+ds1/marshaler_example_test.go000066400000000000000000000020661520243672000242620ustar00rootroot00000000000000package ebpf import ( "encoding" "fmt" "strings" ) // Assert that customEncoding implements the correct interfaces. var ( _ encoding.BinaryMarshaler = (*customEncoding)(nil) _ encoding.BinaryUnmarshaler = (*customEncoding)(nil) ) type customEncoding struct { data string } func (ce *customEncoding) MarshalBinary() ([]byte, error) { return []byte(strings.ToUpper(ce.data)), nil } func (ce *customEncoding) UnmarshalBinary(buf []byte) error { ce.data = string(buf) return nil } // ExampleMarshaler shows how to use custom encoding with map methods. func Example_customMarshaler() { hash, err := NewMap(&MapSpec{ Type: Hash, KeySize: 5, ValueSize: 4, MaxEntries: 10, }) if err != nil { panic(err) } defer hash.Close() if err := hash.Put(&customEncoding{"hello"}, uint32(111)); err != nil { panic(err) } var ( key customEncoding value uint32 entries = hash.Iterate() ) for entries.Next(&key, &value) { fmt.Printf("key: %s, value: %d\n", key.data, value) } if err := entries.Err(); err != nil { panic(err) } } golang-github-cilium-ebpf-0.21.0+ds1/marshalers.go000066400000000000000000000140021520243672000216640ustar00rootroot00000000000000package ebpf import ( "encoding" "errors" "fmt" "reflect" "slices" "unsafe" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/sysenc" ) // marshalMapSyscallInput converts an arbitrary value into a pointer suitable // to be passed to the kernel. // // As an optimization, it returns the original value if it is an // unsafe.Pointer. func marshalMapSyscallInput(data any, length int) (sys.Pointer, error) { if ptr, ok := data.(unsafe.Pointer); ok { return sys.UnsafePointer(ptr), nil } buf, err := sysenc.Marshal(data, length) if err != nil { return sys.Pointer{}, err } return buf.Pointer(), nil } func makeMapSyscallOutput(dst any, length int) sysenc.Buffer { if ptr, ok := dst.(unsafe.Pointer); ok { return sysenc.UnsafeBuffer(ptr) } _, ok := dst.(encoding.BinaryUnmarshaler) if ok { return sysenc.SyscallOutput(nil, length) } return sysenc.SyscallOutput(dst, length) } // appendPerCPUSlice encodes a slice containing one value per // possible CPU into a buffer of bytes. // // Values are initialized to zero if the slice has less elements than CPUs. func appendPerCPUSlice(buf []byte, slice any, possibleCPUs, elemLength, alignedElemLength int) ([]byte, error) { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { return nil, errors.New("per-CPU value requires slice") } sliceValue := reflect.ValueOf(slice) sliceLen := sliceValue.Len() if sliceLen > possibleCPUs { return nil, fmt.Errorf("per-CPU value greater than number of CPUs") } // Grow increases the slice's capacity, _if_necessary_ buf = slices.Grow(buf, alignedElemLength*possibleCPUs) for i := 0; i < sliceLen; i++ { elem := sliceValue.Index(i).Interface() elemBytes, err := sysenc.Marshal(elem, elemLength) if err != nil { return nil, err } buf = elemBytes.AppendTo(buf) buf = append(buf, make([]byte, alignedElemLength-elemLength)...) } // Ensure buf is zero-padded full size. buf = append(buf, make([]byte, (possibleCPUs-sliceLen)*alignedElemLength)...) return buf, nil } // marshalPerCPUValue encodes a slice containing one value per // possible CPU into a buffer of bytes. // // Values are initialized to zero if the slice has less elements than CPUs. func marshalPerCPUValue(slice any, elemLength int) (sys.Pointer, error) { possibleCPUs, err := PossibleCPU() if err != nil { return sys.Pointer{}, err } alignedElemLength := internal.Align(elemLength, 8) buf := make([]byte, 0, alignedElemLength*possibleCPUs) buf, err = appendPerCPUSlice(buf, slice, possibleCPUs, elemLength, alignedElemLength) if err != nil { return sys.Pointer{}, err } return sys.UnsafeSlicePointer(buf), nil } // marshalBatchPerCPUValue encodes a batch-sized slice of slices containing // one value per possible CPU into a buffer of bytes. func marshalBatchPerCPUValue(slice any, batchLen, elemLength int) ([]byte, error) { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { return nil, fmt.Errorf("batch value requires a slice") } sliceValue := reflect.ValueOf(slice) possibleCPUs, err := PossibleCPU() if err != nil { return nil, err } if sliceValue.Len() != batchLen*possibleCPUs { return nil, fmt.Errorf("per-CPU slice has incorrect length, expected %d, got %d", batchLen*possibleCPUs, sliceValue.Len()) } alignedElemLength := internal.Align(elemLength, 8) buf := make([]byte, 0, batchLen*alignedElemLength*possibleCPUs) for i := 0; i < batchLen; i++ { batch := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface() buf, err = appendPerCPUSlice(buf, batch, possibleCPUs, elemLength, alignedElemLength) if err != nil { return nil, fmt.Errorf("batch %d: %w", i, err) } } return buf, nil } // unmarshalPerCPUValue decodes a buffer into a slice containing one value per // possible CPU. // // slice must be a literal slice and not a pointer. func unmarshalPerCPUValue(slice any, elemLength int, buf []byte) error { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { return fmt.Errorf("per-CPU value requires a slice") } possibleCPUs, err := PossibleCPU() if err != nil { return err } sliceValue := reflect.ValueOf(slice) if sliceValue.Len() != possibleCPUs { return fmt.Errorf("per-CPU slice has incorrect length, expected %d, got %d", possibleCPUs, sliceValue.Len()) } sliceElemType := sliceType.Elem() sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr stride := internal.Align(elemLength, 8) for i := 0; i < possibleCPUs; i++ { var elem any v := sliceValue.Index(i) if sliceElemIsPointer { if !v.Elem().CanAddr() { return fmt.Errorf("per-CPU slice elements cannot be nil") } elem = v.Elem().Addr().Interface() } else { elem = v.Addr().Interface() } err := sysenc.Unmarshal(elem, buf[:elemLength]) if err != nil { return fmt.Errorf("cpu %d: %w", i, err) } buf = buf[stride:] } return nil } // unmarshalBatchPerCPUValue decodes a buffer into a batch-sized slice // containing one value per possible CPU. // // slice must have length batchLen * PossibleCPUs(). func unmarshalBatchPerCPUValue(slice any, batchLen, elemLength int, buf []byte) error { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { return fmt.Errorf("batch requires a slice") } sliceValue := reflect.ValueOf(slice) possibleCPUs, err := PossibleCPU() if err != nil { return err } if sliceValue.Len() != batchLen*possibleCPUs { return fmt.Errorf("per-CPU slice has incorrect length, expected %d, got %d", sliceValue.Len(), batchLen*possibleCPUs) } fullValueSize := possibleCPUs * internal.Align(elemLength, 8) if len(buf) != batchLen*fullValueSize { return fmt.Errorf("input buffer has incorrect length, expected %d, got %d", len(buf), batchLen*fullValueSize) } for i := 0; i < batchLen; i++ { elem := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface() if err := unmarshalPerCPUValue(elem, elemLength, buf[:fullValueSize]); err != nil { return fmt.Errorf("batch %d: %w", i, err) } buf = buf[fullValueSize:] } return nil } golang-github-cilium-ebpf-0.21.0+ds1/marshalers_test.go000066400000000000000000000067771520243672000227470ustar00rootroot00000000000000package ebpf import ( "testing" "github.com/cilium/ebpf/internal" "github.com/go-quicktest/qt" ) func TestMarshalUnmarshalBatchPerCPUValue(t *testing.T) { const ( batchLen = 3 elemLength = 4 ) possibleCPU := MustPossibleCPU() sliceLen := batchLen * possibleCPU slice := makeFilledSlice(sliceLen) buf, err := marshalBatchPerCPUValue(slice, batchLen, elemLength) if err != nil { t.Fatal(err) } output := make([]uint32, sliceLen) err = unmarshalBatchPerCPUValue(output, batchLen, elemLength, buf) if err != nil { t.Fatal(err) } qt.Assert(t, qt.DeepEquals(output, slice)) } func TestMarshalBatchPerCPUValue(t *testing.T) { const ( batchLen = 3 elemLength = 4 ) possibleCPU := MustPossibleCPU() sliceLen := batchLen * possibleCPU slice := makeFilledSlice(sliceLen) expected := make([]byte, sliceLen*internal.Align(elemLength, 8)) b := expected for _, elem := range slice { internal.NativeEndian.PutUint32(b, elem) b = b[8:] } buf, err := marshalBatchPerCPUValue(slice, batchLen, elemLength) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(buf, expected)) tooSmall := slice[:len(slice)-1] buf, err = marshalBatchPerCPUValue(tooSmall, batchLen, elemLength) qt.Assert(t, qt.IsNotNil(err)) qt.Assert(t, qt.HasLen(buf, 0)) tooBig := append(slice, 0) buf, err = marshalBatchPerCPUValue(tooBig, batchLen, elemLength) qt.Assert(t, qt.IsNotNil(err)) qt.Assert(t, qt.HasLen(buf, 0)) } func TestUnmarshalBatchPerCPUValue(t *testing.T) { const ( batchLen = 3 elemLength = 4 ) possibleCPU := MustPossibleCPU() outputLen := batchLen * possibleCPU output := make([]uint32, outputLen) expected := makeFilledSlice(batchLen * possibleCPU) buf := make([]byte, batchLen*possibleCPU*internal.Align(elemLength, 8)) b := buf for _, elem := range expected { internal.NativeEndian.PutUint32(b, elem) b = b[8:] } err := unmarshalBatchPerCPUValue(output, batchLen, elemLength, buf) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.DeepEquals(output, expected)) tooSmall := make([]uint32, outputLen-1) err = unmarshalBatchPerCPUValue(tooSmall, batchLen, elemLength, buf) qt.Assert(t, qt.IsNotNil(err)) tooBig := make([]uint32, outputLen+1) err = unmarshalBatchPerCPUValue(tooBig, batchLen, elemLength, buf) qt.Assert(t, qt.IsNotNil(err)) empty := make([]uint32, outputLen) tooSmallBuf := buf[:len(buf)-1] err = unmarshalBatchPerCPUValue(empty, batchLen, elemLength, tooSmallBuf) qt.Assert(t, qt.IsNotNil(err)) tooBigBuf := append(buf, 0) err = unmarshalBatchPerCPUValue(empty, batchLen, elemLength, tooBigBuf) qt.Assert(t, qt.IsNotNil(err)) } func TestUnmarshalPerCPUValue(t *testing.T) { possibleCPUs := MustPossibleCPU() expected := make([]uint32, possibleCPUs) for i := 0; i < possibleCPUs; i++ { expected[i] = uint32(1021 * (i + 1)) } elemLength := 4 buf := make([]byte, possibleCPUs*internal.Align(elemLength, 8)) b := buf for _, elem := range expected { internal.NativeEndian.PutUint32(b, elem) b = b[8:] } slice := make([]uint32, possibleCPUs) err := unmarshalPerCPUValue(slice, elemLength, buf) if err != nil { t.Fatal(err) } qt.Assert(t, qt.DeepEquals(slice, expected)) smallSlice := make([]uint32, possibleCPUs-1) qt.Assert(t, qt.IsNotNil(unmarshalPerCPUValue(smallSlice, elemLength, buf))) nilElemSlice := make([]*uint32, possibleCPUs) qt.Assert(t, qt.IsNotNil(unmarshalPerCPUValue(nilElemSlice, elemLength, buf))) } func makeFilledSlice(len int) []uint32 { slice := make([]uint32, len) for i := range slice { slice[i] = uint32(1021 * (i + 1)) } return slice } golang-github-cilium-ebpf-0.21.0+ds1/memory.go000066400000000000000000000110201520243672000210300ustar00rootroot00000000000000package ebpf import ( "errors" "fmt" "io" "runtime" "github.com/cilium/ebpf/internal/unix" ) // Memory is the building block for accessing the memory of specific bpf map // types (Array and Arena at the time of writing) without going through the bpf // syscall interface. // // Given the fd of a bpf map created with the BPF_F_MMAPABLE flag, a shared // 'file'-based memory-mapped region can be allocated in the process' address // space, exposing the bpf map's memory by simply accessing a memory location. var ErrReadOnly = errors.New("resource is read-only") // Memory implements accessing a Map's memory without making any syscalls. // Pay attention to the difference between Go and C struct alignment rules. Use // [structs.HostLayout] on supported Go versions to help with alignment. // // Note on memory coherence: avoid using packed structs in memory shared between // user space and eBPF C programs. This drops a struct's memory alignment to 1, // forcing the compiler to use single-byte loads and stores for field accesses. // This may lead to partially-written data to be observed from user space. // // On most architectures, the memmove implementation used by Go's copy() will // access data in word-sized chunks. If paired with a matching access pattern on // the eBPF C side (and if using default memory alignment), accessing shared // memory without atomics or other synchronization primitives should be sound // for individual values. For accesses beyond a single value, the usual // concurrent programming rules apply. type Memory struct { b []byte ro bool heap bool cleanup runtime.Cleanup } func newMemory(fd, size int) (*Memory, error) { // Typically, maps created with BPF_F_RDONLY_PROG remain writable from user // space until frozen. As a security precaution, the kernel doesn't allow // mapping bpf map memory as read-write into user space if the bpf map was // frozen, or if it was created using the RDONLY_PROG flag. // // The user would be able to write to the map after freezing (since the kernel // can't change the protection mode of an already-mapped page), while the // verifier assumes the contents to be immutable. b, err := unix.Mmap(fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) // If the map is frozen when an rw mapping is requested, expect EPERM. If the // map was created with BPF_F_RDONLY_PROG, expect EACCES. var ro bool if errors.Is(err, unix.EPERM) || errors.Is(err, unix.EACCES) { ro = true b, err = unix.Mmap(fd, 0, size, unix.PROT_READ, unix.MAP_SHARED) } if err != nil { return nil, fmt.Errorf("setting up memory-mapped region: %w", err) } mm := &Memory{b: b, ro: ro, heap: false} mm.cleanup = runtime.AddCleanup(mm, memoryCleanupFunc(), b) return mm, nil } func memoryCleanupFunc() func([]byte) { return func(b []byte) { if err := unix.Munmap(b); err != nil { panic(fmt.Errorf("unmapping memory: %w", err)) } } } func (mm *Memory) close() { mm.cleanup.Stop() memoryCleanupFunc()(mm.b) mm.b = nil } // Size returns the size of the memory-mapped region in bytes. func (mm *Memory) Size() uint32 { return uint32(len(mm.b)) } // ReadOnly returns true if the memory-mapped region is read-only. func (mm *Memory) ReadOnly() bool { return mm.ro } // bounds returns true if an access at off of the given size is within bounds. func (mm *Memory) bounds(off, size uint32) bool { if off+size < off { return false } return off+size <= uint32(len(mm.b)) } // ReadAt implements [io.ReaderAt]. Useful for creating a new [io.OffsetWriter]. // // See [Memory] for details around memory coherence. func (mm *Memory) ReadAt(p []byte, off int64) (int, error) { if mm.b == nil { return 0, fmt.Errorf("memory-mapped region closed") } if p == nil { return 0, fmt.Errorf("input buffer p is nil") } if off < 0 || off >= int64(len(mm.b)) { return 0, fmt.Errorf("read offset out of range") } n := copy(p, mm.b[off:]) if n < len(p) { return n, io.EOF } return n, nil } // WriteAt implements [io.WriterAt]. Useful for creating a new // [io.SectionReader]. // // See [Memory] for details around memory coherence. func (mm *Memory) WriteAt(p []byte, off int64) (int, error) { if mm.b == nil { return 0, fmt.Errorf("memory-mapped region closed") } if mm.ro { return 0, fmt.Errorf("memory-mapped region not writable: %w", ErrReadOnly) } if p == nil { return 0, fmt.Errorf("output buffer p is nil") } if off < 0 || off >= int64(len(mm.b)) { return 0, fmt.Errorf("write offset out of range") } n := copy(mm.b[off:], p) if n < len(p) { return n, io.EOF } return n, nil } golang-github-cilium-ebpf-0.21.0+ds1/memory_test.go000066400000000000000000000046131520243672000221010ustar00rootroot00000000000000package ebpf import ( "io" "math" "os" "runtime" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" ) func mustMmapableArray(tb testing.TB, extraFlags uint32) *Map { tb.Helper() m, err := newMap(tb, &MapSpec{ Name: "ebpf_mmap", Type: Array, KeySize: 4, ValueSize: 8, MaxEntries: 8, Flags: sys.BPF_F_MMAPABLE | extraFlags, }, nil) testutils.SkipIfNotSupported(tb, err) qt.Assert(tb, qt.IsNil(err)) return m } func TestMemory(t *testing.T) { mm, err := mustMmapableArray(t, 0).Memory() qt.Assert(t, qt.IsNil(err)) // Ensure the cleanup is set correctly and doesn't unmap the region while // we're using it. runtime.GC() // The mapping is always at least one page long, and the Map created here fits // in a single page. qt.Assert(t, qt.Equals(mm.Size(), uint32(os.Getpagesize()))) // No BPF_F_RDONLY_PROG flag, so the Memory should be read-write. qt.Assert(t, qt.IsFalse(mm.ReadOnly())) want := []byte{1, 2, 3, 4, 4, 3, 2, 1} w := io.NewOffsetWriter(mm, 16) n, err := w.Write(want) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(n, 8)) r := io.NewSectionReader(mm, 16, int64(len(want))) got := make([]byte, len(want)) n, err = r.Read(got) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(n, len(want))) } func TestMemoryBounds(t *testing.T) { mm, err := mustMmapableArray(t, 0).Memory() qt.Assert(t, qt.IsNil(err)) end := mm.Size() qt.Assert(t, qt.IsTrue(mm.bounds(0, 0))) qt.Assert(t, qt.IsTrue(mm.bounds(end, 0))) qt.Assert(t, qt.IsTrue(mm.bounds(end-8, 8))) qt.Assert(t, qt.IsTrue(mm.bounds(0, end))) qt.Assert(t, qt.IsFalse(mm.bounds(end-8, 9))) qt.Assert(t, qt.IsFalse(mm.bounds(end, 1))) qt.Assert(t, qt.IsFalse(mm.bounds(math.MaxUint32, 1))) } func TestMemoryReadOnly(t *testing.T) { rd, err := mustMmapableArray(t, sys.BPF_F_RDONLY_PROG).Memory() qt.Assert(t, qt.IsNil(err)) // BPF_F_RDONLY_PROG flag, so the Memory should be read-only. qt.Assert(t, qt.IsTrue(rd.ReadOnly())) // Frozen maps can't be mapped rw either. frozen := mustMmapableArray(t, 0) qt.Assert(t, qt.IsNil(frozen.Freeze())) fz, err := frozen.Memory() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(fz.ReadOnly())) } func TestMemoryClose(t *testing.T) { mm, err := mustMmapableArray(t, 0).Memory() qt.Assert(t, qt.IsNil(err)) // unmap panics if the operation fails. mm.close() } golang-github-cilium-ebpf-0.21.0+ds1/memory_unsafe.go000066400000000000000000000325211520243672000224020ustar00rootroot00000000000000package ebpf import ( "errors" "fmt" "os" "reflect" "runtime" "unsafe" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/unix" ) // This file contains an experimental, unsafe implementation of Memory that // allows taking a Go pointer to a memory-mapped region. This currently does not // have first-class support from the Go runtime, so it may break in future Go // versions. The Go proposal for the runtime to track off-heap pointers is here: // https://github.com/golang/go/issues/70224. // // In Go, the programmer should not have to worry about freeing memory. Since // this API synthesizes Go variables around global variables declared in a BPF // C program, we want to lean on the runtime for making sure accessing them is // safe at all times. Unfortunately, Go (as of 1.24) does not have the ability // of automatically managing memory that was not allocated by the runtime. // // This led to a solution that requests regular Go heap memory by allocating a // slice (making the runtime track pointers into the slice's backing array) and // memory-mapping the bpf map's memory over it. Then, before returning the // Memory to the caller, a finalizer is set on the backing array, making sure // the bpf map's memory is unmapped from the heap before releasing the backing // array to the runtime for reallocation. // // This obviates the need to maintain a reference to the *Memory at all times, // which is difficult for the caller to achieve if the variable access is done // through another object (like a sync.Atomic) that can potentially be passed // around the Go application. Accidentally losing the reference to the *Memory // would result in hard-to-debug segfaults, which are always unexpected in Go. //go:linkname heapObjectsCanMove runtime.heapObjectsCanMove func heapObjectsCanMove() bool // Set from a file behind the ebpf_unsafe_memory_experiment build tag to enable // features that require mapping bpf map memory over the Go heap. var unsafeMemory = false // ErrInvalidType is returned when the given type cannot be used as a Memory or // Variable pointer. var ErrInvalidType = errors.New("invalid type") func newUnsafeMemory(fd, size int) (*Memory, error) { // Some architectures need the size to be page-aligned to work with MAP_FIXED. if size%os.Getpagesize() != 0 { return nil, fmt.Errorf("memory: must be a multiple of page size (requested %d bytes)", size) } // Allocate a page-aligned span of memory on the Go heap. alloc, err := allocate(size) if err != nil { return nil, fmt.Errorf("allocating memory: %w", err) } // Typically, maps created with BPF_F_RDONLY_PROG remain writable from user // space until frozen. As a security precaution, the kernel doesn't allow // mapping bpf map memory as read-write into user space if the bpf map was // frozen, or if it was created using the RDONLY_PROG flag. // // The user would be able to write to the map after freezing (since the kernel // can't change the protection mode of an already-mapped page), while the // verifier assumes the contents to be immutable. // // Map the bpf map memory over a page-aligned allocation on the Go heap. err = mapmap(fd, alloc, size, unix.PROT_READ|unix.PROT_WRITE) // If the map is frozen when an rw mapping is requested, expect EPERM. If the // map was created with BPF_F_RDONLY_PROG, expect EACCES. var ro bool if errors.Is(err, unix.EPERM) || errors.Is(err, unix.EACCES) { ro = true err = mapmap(fd, alloc, size, unix.PROT_READ) } if err != nil { return nil, fmt.Errorf("setting up memory-mapped region: %w", err) } mm := &Memory{ unsafe.Slice((*byte)(alloc), size), ro, true, runtime.Cleanup{}, } return mm, nil } // allocate returns a pointer to a page-aligned section of memory on the Go // heap, managed by the runtime. // //go:nocheckptr func allocate(size int) (unsafe.Pointer, error) { // Memory-mapping over a piece of the Go heap is unsafe when the GC can // randomly decide to move objects around, in which case the mapped region // will not move along with it. if heapObjectsCanMove() { return nil, errors.New("this Go runtime has a moving garbage collector") } if size == 0 { return nil, errors.New("size must be greater than 0") } // Request at least two pages of memory from the runtime to ensure we can // align the requested allocation to a page boundary. This is needed for // MAP_FIXED and makes sure we don't mmap over some other allocation on the Go // heap. size = internal.Align(size+os.Getpagesize(), os.Getpagesize()) // Allocate a new slice and store a pointer to its backing array. alloc := unsafe.Pointer(unsafe.SliceData(make([]byte, size))) // nolint:govet // // Align the pointer to a page boundary within the allocation. This may alias // the initial pointer if it was already page-aligned. Ignore govet warnings // since we're calling [runtime.KeepAlive] on the original Go memory. aligned := unsafe.Pointer(internal.Align(uintptr(alloc), uintptr(os.Getpagesize()))) runtime.KeepAlive(alloc) // Return an aligned pointer into the backing array, losing the original // reference. The runtime.SetFinalizer docs specify that its argument 'must be // a pointer to an object, complit or local var', but this is still somewhat // vague and not enforced by the current implementation. // // Currently, finalizers can be set and triggered from any address within a // heap allocation, even individual struct fields or arbitrary offsets within // a slice. In this case, finalizers set on struct fields or slice offsets // will only run when the whole struct or backing array are collected. The // accepted runtime.AddCleanup proposal makes this behaviour more explicit and // is set to deprecate runtime.SetFinalizer. // // Alternatively, we'd have to track the original allocation and the aligned // pointer separately, which severely complicates finalizer setup and makes it // prone to human error. For now, just bump the pointer and treat it as the // new and only reference to the backing array. return aligned, nil } // mapmap memory-maps the given file descriptor at the given address and sets a // finalizer on addr to unmap it when it's no longer reachable. func mapmap(fd int, addr unsafe.Pointer, size, flags int) error { // Map the bpf map memory over the Go heap. This will result in the following // mmap layout in the process' address space (0xc000000000 is a span of Go // heap), visualized using pmap: // // Address Kbytes RSS Dirty Mode Mapping // 000000c000000000 1824 864 864 rw--- [ anon ] // 000000c0001c8000 4 4 4 rw-s- [ anon ] // 000000c0001c9000 2268 16 16 rw--- [ anon ] // // This will break up the Go heap, but as long as the runtime doesn't try to // move our allocation around, this is safe for as long as we hold a reference // to our allocated object. // // Use MAP_SHARED to make sure the kernel sees any writes we do, and MAP_FIXED // to ensure the mapping starts exactly at the address we requested. If alloc // isn't page-aligned, the mapping operation will fail. if _, err := unix.MmapPtr(fd, 0, addr, uintptr(size), flags, unix.MAP_SHARED|unix.MAP_FIXED); err != nil { return fmt.Errorf("setting up memory-mapped region: %w", err) } // Set a finalizer on the heap allocation to undo the mapping before the span // is collected and reused by the runtime. This has a few reasons: // // - Avoid leaking memory/mappings. // - Future writes to this memory should never clobber a bpf map's contents. // - Some bpf maps are mapped read-only, causing a segfault if the runtime // reallocates and zeroes the span later. runtime.SetFinalizer((*byte)(addr), unmap(size)) return nil } // unmap returns a function that takes a pointer to a memory-mapped region on // the Go heap. The function undoes any mappings and discards the span's // contents. // // Used as a finalizer in [newMemory], split off into a separate function for // testing and to avoid accidentally closing over the unsafe.Pointer to the // memory region, which would cause a cyclical reference. // // The resulting function panics if the mmap operation returns an error, since // it would mean the integrity of the Go heap is compromised. func unmap(size int) func(*byte) { return func(a *byte) { // Create another mapping at the same address to undo the original mapping. // This will cause the kernel to repair the slab since we're using the same // protection mode and flags as the original mapping for the Go heap. // // Address Kbytes RSS Dirty Mode Mapping // 000000c000000000 4096 884 884 rw--- [ anon ] // // Using munmap here would leave an unmapped hole in the heap, compromising // its integrity. // // MmapPtr allocates another unsafe.Pointer at the same address. Even though // we discard it here, it may temporarily resurrect the backing array and // delay its collection to the next GC cycle. _, err := unix.MmapPtr(-1, 0, unsafe.Pointer(a), uintptr(size), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_FIXED|unix.MAP_ANON) if err != nil { panic(fmt.Errorf("undoing bpf map memory mapping: %w", err)) } } } // checkUnsafeMemory ensures value T can be accessed in mm at offset off. // // The comparable constraint narrows down the set of eligible types to exclude // slices, maps and functions. These complex types cannot be mapped to memory // directly. func checkUnsafeMemory[T comparable](mm *Memory, off uint32) error { if mm.b == nil { return fmt.Errorf("memory-mapped region is nil") } if mm.ro { return ErrReadOnly } if !mm.heap { return fmt.Errorf("memory region is not heap-mapped, build with '-tags ebpf_unsafe_memory_experiment' to enable: %w", ErrNotSupported) } t := reflect.TypeFor[T]() if err := checkType(t.String(), t); err != nil { return err } size := t.Size() if size == 0 { return fmt.Errorf("zero-sized type %s: %w", t, ErrInvalidType) } if off%uint32(t.Align()) != 0 { return fmt.Errorf("unaligned access of memory-mapped region: %d-byte aligned read at offset %d", t.Align(), off) } vs, bs := uint32(size), uint32(len(mm.b)) if off+vs > bs { return fmt.Errorf("%d-byte value at offset %d exceeds mmap size of %d bytes", vs, off, bs) } return nil } // checkType recursively checks if the given type is supported for memory // mapping. Only fixed-size, non-Go-pointer types are supported: bools, floats, // (u)int[8-64], arrays, and structs containing them. As an exception, uintptr // is allowed since the backing memory is expected to contain 32-bit pointers on // 32-bit systems despite BPF always allocating 64 bits for pointers in a data // section. // // Doesn't check for loops since it rejects pointers. Should that ever change, a // visited set would be needed. func checkType(name string, t reflect.Type) error { // Special-case atomic types to allow them to be used as root types as well as // struct fields. Notably, omit atomic.Value and atomic.Pointer since those // are pointer types. Also, atomic.Value embeds an interface value, which // doesn't make sense to share with C land. if t.PkgPath() == "sync/atomic" { switch t.Name() { case "Bool", "Int32", "Int64", "Uint32", "Uint64", "Uintptr": return nil } } switch t.Kind() { case reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return nil case reflect.Array: at := t.Elem() if err := checkType(fmt.Sprintf("%s.%s", name, at.String()), at); err != nil { return err } case reflect.Struct: var hasHostLayout bool for i := range t.NumField() { at := t.Field(i).Type // Require [structs.HostLayout] to be embedded in all structs. Check the // full package path to reject a user-defined HostLayout type. if at.PkgPath() == "structs" && at.Name() == "HostLayout" { hasHostLayout = true continue } if err := checkType(fmt.Sprintf("%s.%s", name, at.String()), at); err != nil { return err } } if !hasHostLayout { return fmt.Errorf("struct %s must embed structs.HostLayout: %w", name, ErrInvalidType) } default: // For basic types like int and bool, the kind name is the same as the type // name, so the fallthrough case would print 'int type int not supported'. // Omit the kind name if it matches the type name. if t.String() == t.Kind().String() { // Output: type int not supported return fmt.Errorf("type %s not supported: %w", name, ErrInvalidType) } // Output: interface value io.Reader not supported return fmt.Errorf("%s type %s not supported: %w", t.Kind(), name, ErrInvalidType) } return nil } // memoryPointer returns a pointer to a value of type T at offset off in mm. // Taking a pointer to a read-only Memory or to a Memory that is not heap-mapped // is not supported. // // T must contain only fixed-size, non-Go-pointer types: bools, floats, // (u)int[8-64], arrays, and structs containing them. Structs must embed // [structs.HostLayout]. [ErrInvalidType] is returned if T is not a valid type. // // Memory must be writable, off must be aligned to the size of T, and the value // must be within bounds of the Memory. // // To access read-only memory, use [Memory.ReadAt]. func memoryPointer[T comparable](mm *Memory, off uint32) (*T, error) { if err := checkUnsafeMemory[T](mm, off); err != nil { return nil, fmt.Errorf("memory pointer: %w", err) } return (*T)(unsafe.Pointer(&mm.b[off])), nil } golang-github-cilium-ebpf-0.21.0+ds1/memory_unsafe_tag.go000066400000000000000000000001351520243672000232310ustar00rootroot00000000000000//go:build ebpf_unsafe_memory_experiment package ebpf func init() { unsafeMemory = true } golang-github-cilium-ebpf-0.21.0+ds1/memory_unsafe_test.go000066400000000000000000000077641520243672000234540ustar00rootroot00000000000000package ebpf import ( "runtime" "structs" "sync/atomic" "testing" "unsafe" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/sys" ) func TestUnsafeMemoryDisabled(t *testing.T) { mm, err := mustMmapableArray(t, 0).Memory() qt.Assert(t, qt.IsNil(err)) _, err = memoryPointer[uint32](mm, 0) qt.Assert(t, qt.ErrorIs(err, ErrNotSupported)) } func TestUnsafeMemoryUnmap(t *testing.T) { mm, err := mustMmapableArray(t, 0).unsafeMemory() qt.Assert(t, qt.IsNil(err)) // Avoid unmap running twice. runtime.SetFinalizer(unsafe.SliceData(mm.b), nil) // unmap panics if the operation fails. unmap(int(mm.Size()))(unsafe.SliceData(mm.b)) } func TestUnsafeMemoryPointer(t *testing.T) { m := mustMmapableArray(t, 0) mm, err := m.unsafeMemory() qt.Assert(t, qt.IsNil(err)) // Requesting an unaligned value should fail. _, err = memoryPointer[uint32](mm, 7) qt.Assert(t, qt.IsNotNil(err)) u64, err := memoryPointer[uint64](mm, 8) qt.Assert(t, qt.IsNil(err)) want := uint64(0xf00d) *u64 = want qt.Assert(t, qt.Equals(*u64, want)) // Read back the value using the bpf syscall interface. var got uint64 qt.Assert(t, qt.IsNil(m.Lookup(uint32(1), &got))) qt.Assert(t, qt.Equals(got, want)) _, err = memoryPointer[*uint32](mm, 0) qt.Assert(t, qt.ErrorIs(err, ErrInvalidType)) } func TestUnsafeMemoryReadOnly(t *testing.T) { rd, err := mustMmapableArray(t, sys.BPF_F_RDONLY_PROG).unsafeMemory() qt.Assert(t, qt.IsNil(err)) // BPF_F_RDONLY_PROG flag, so the Memory should be read-only. qt.Assert(t, qt.IsTrue(rd.ReadOnly())) // Frozen maps can't be mapped rw either. frozen := mustMmapableArray(t, 0) qt.Assert(t, qt.IsNil(frozen.Freeze())) fz, err := frozen.Memory() qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(fz.ReadOnly())) _, err = fz.WriteAt([]byte{1}, 0) qt.Assert(t, qt.ErrorIs(err, ErrReadOnly)) _, err = memoryPointer[uint32](fz, 0) qt.Assert(t, qt.ErrorIs(err, ErrReadOnly)) } func TestCheckUnsafeMemory(t *testing.T) { mm, err := mustMmapableArray(t, 0).unsafeMemory() qt.Assert(t, qt.IsNil(err)) // Primitive types qt.Assert(t, qt.IsNil(checkUnsafeMemory[bool](mm, 0))) qt.Assert(t, qt.IsNil(checkUnsafeMemory[uint32](mm, 0))) // Arrays qt.Assert(t, qt.IsNil(checkUnsafeMemory[[4]byte](mm, 0))) qt.Assert(t, qt.IsNil(checkUnsafeMemory[[2]struct { _ structs.HostLayout A uint32 B uint64 }](mm, 0))) // Structs qt.Assert(t, qt.IsNil(checkUnsafeMemory[struct { _ structs.HostLayout _ uint32 }](mm, 0))) qt.Assert(t, qt.IsNil(checkUnsafeMemory[struct { _ structs.HostLayout _ [4]byte }](mm, 0))) // Atomics qt.Assert(t, qt.IsNil(checkUnsafeMemory[atomic.Uint32](mm, 0))) qt.Assert(t, qt.IsNil(checkUnsafeMemory[struct { _ structs.HostLayout _ atomic.Uint32 }](mm, 0))) // Special cases qt.Assert(t, qt.IsNil(checkUnsafeMemory[uintptr](mm, 0))) qt.Assert(t, qt.IsNil(checkUnsafeMemory[atomic.Uintptr](mm, 0))) qt.Assert(t, qt.IsNil(checkUnsafeMemory[struct { _ structs.HostLayout _ uintptr }](mm, 0))) // No pointers qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[*uint32](mm, 0), ErrInvalidType)) qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[**uint32](mm, 0), ErrInvalidType)) qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[[1]*uint8](mm, 0), ErrInvalidType)) qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[struct { _ structs.HostLayout _ *uint8 }](mm, 0), ErrInvalidType)) qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[atomic.Pointer[uint64]](mm, 0), ErrInvalidType)) qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[atomic.Value](mm, 0), ErrInvalidType)) // No variable-sized types qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[int](mm, 0), ErrInvalidType)) qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[uint](mm, 0), ErrInvalidType)) // No interface types qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[any](mm, 0), ErrInvalidType)) // No zero-sized types qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[struct{ _ structs.HostLayout }](mm, 0), ErrInvalidType)) // No structs without HostLayout qt.Assert(t, qt.ErrorIs(checkUnsafeMemory[struct{ _ uint32 }](mm, 0), ErrInvalidType)) } golang-github-cilium-ebpf-0.21.0+ds1/netlify.toml000066400000000000000000000001641520243672000215470ustar00rootroot00000000000000[build] base = "docs/" publish = "site/" command = "mkdocs build" environment = { PYTHON_VERSION = "3.13" } golang-github-cilium-ebpf-0.21.0+ds1/perf/000077500000000000000000000000001520243672000201335ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/perf/doc.go000066400000000000000000000003151520243672000212260ustar00rootroot00000000000000// Package perf allows reading from BPF perf event arrays. // // A perf event array contains multiple perf event ringbuffers which can be used // to exchange sample like data with user space. package perf golang-github-cilium-ebpf-0.21.0+ds1/perf/reader.go000066400000000000000000000315441520243672000217330ustar00rootroot00000000000000//go:build !windows package perf import ( "encoding/binary" "errors" "fmt" "io" "os" "sync" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/epoll" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) var ( ErrClosed = os.ErrClosed ErrFlushed = epoll.ErrFlushed errEOR = errors.New("end of ring") ) var perfEventHeaderSize = binary.Size(perfEventHeader{}) // perfEventHeader must match 'struct perf_event_header` in . type perfEventHeader struct { Type uint32 Misc uint16 Size uint16 } // Record contains either a sample or a counter of the // number of lost samples. type Record struct { // The CPU this record was generated on. CPU int // The data submitted via bpf_perf_event_output. // Due to a kernel bug, this can contain between 0 and 7 bytes of trailing // garbage from the ring depending on the input sample's length. RawSample []byte // The number of samples which could not be output, since // the ring buffer was full. LostSamples uint64 // The minimum number of bytes remaining in the per-CPU buffer after this Record has been read. // Negative for overwritable buffers. Remaining int } // Read a record from a reader and tag it as being from the given CPU. // // buf must be at least perfEventHeaderSize bytes long. func readRecord(rd io.Reader, rec *Record, buf []byte, overwritable bool) error { // Assert that the buffer is large enough. buf = buf[:perfEventHeaderSize] _, err := io.ReadFull(rd, buf) if errors.Is(err, io.EOF) { return errEOR } else if err != nil { return fmt.Errorf("read perf event header: %v", err) } header := perfEventHeader{ internal.NativeEndian.Uint32(buf[0:4]), internal.NativeEndian.Uint16(buf[4:6]), internal.NativeEndian.Uint16(buf[6:8]), } switch header.Type { case unix.PERF_RECORD_LOST: rec.RawSample = rec.RawSample[:0] rec.LostSamples, err = readLostRecords(rd) return err case unix.PERF_RECORD_SAMPLE: rec.LostSamples = 0 // We can reuse buf here because perfEventHeaderSize > perfEventSampleSize. rec.RawSample, err = readRawSample(rd, buf, rec.RawSample) return err default: return &unknownEventError{header.Type} } } func readLostRecords(rd io.Reader) (uint64, error) { // lostHeader must match 'struct perf_event_lost in kernel sources. var lostHeader struct { ID uint64 Lost uint64 } err := binary.Read(rd, internal.NativeEndian, &lostHeader) if err != nil { return 0, fmt.Errorf("can't read lost records header: %v", err) } return lostHeader.Lost, nil } var perfEventSampleSize = binary.Size(uint32(0)) // This must match 'struct perf_event_sample in kernel sources. type perfEventSample struct { Size uint32 } func readRawSample(rd io.Reader, buf, sampleBuf []byte) ([]byte, error) { buf = buf[:perfEventSampleSize] if _, err := io.ReadFull(rd, buf); err != nil { return nil, fmt.Errorf("read sample size: %w", err) } sample := perfEventSample{ internal.NativeEndian.Uint32(buf), } var data []byte if size := int(sample.Size); cap(sampleBuf) < size { data = make([]byte, size) } else { data = sampleBuf[:size] } if _, err := io.ReadFull(rd, data); err != nil { return nil, fmt.Errorf("read sample: %w", err) } return data, nil } // Reader allows reading bpf_perf_event_output // from user space. type Reader struct { poller *epoll.Poller // mu protects read/write access to the Reader structure with the // exception fields protected by 'pauseMu'. // If locking both 'mu' and 'pauseMu', 'mu' must be locked first. mu sync.Mutex array *ebpf.Map rings []*perfEventRing epollEvents []unix.EpollEvent epollRings []*perfEventRing eventHeader []byte deadline time.Time overwritable bool bufferSize int pendingErr error // pauseMu protects eventFds so that Pause / Resume can be invoked while // Read is blocked. pauseMu sync.Mutex eventFds []*sys.FD paused bool } // ReaderOptions control the behaviour of the user // space reader. type ReaderOptions struct { // The number of events required in any per CPU buffer before // Read will process data. This is mutually exclusive with Watermark. // The default is zero, which means Watermark will take precedence. WakeupEvents int // The number of written bytes required in any per CPU buffer before // Read will process data. Must be smaller than PerCPUBuffer. // The default is to start processing as soon as data is available. Watermark int // This perf ring buffer is overwritable, once full the oldest event will be // overwritten by newest. Overwritable bool } // NewReader creates a new reader with default options. // // array must be a PerfEventArray. perCPUBuffer gives the size of the // per CPU buffer in bytes. It is rounded up to the nearest multiple // of the current page size. func NewReader(array *ebpf.Map, perCPUBuffer int) (*Reader, error) { return NewReaderWithOptions(array, perCPUBuffer, ReaderOptions{}) } // NewReaderWithOptions creates a new reader with the given options. func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) (pr *Reader, err error) { closeOnError := func(c io.Closer) { if err != nil { c.Close() } } if perCPUBuffer < 1 { return nil, errors.New("perCPUBuffer must be larger than 0") } if opts.WakeupEvents > 0 && opts.Watermark > 0 { return nil, errors.New("WakeupEvents and Watermark cannot both be non-zero") } var ( nCPU = int(array.MaxEntries()) rings = make([]*perfEventRing, 0, nCPU) eventFds = make([]*sys.FD, 0, nCPU) ) poller, err := epoll.New() if err != nil { return nil, err } defer closeOnError(poller) // bpf_perf_event_output checks which CPU an event is enabled on, // but doesn't allow using a wildcard like -1 to specify "all CPUs". // Hence we have to create a ring for each CPU. bufferSize := 0 for i := 0; i < nCPU; i++ { event, ring, err := newPerfEventRing(i, perCPUBuffer, opts) if errors.Is(err, unix.ENODEV) { // The requested CPU is currently offline, skip it. continue } if err != nil { return nil, fmt.Errorf("failed to create perf ring for CPU %d: %v", i, err) } defer closeOnError(event) defer closeOnError(ring) bufferSize = ring.size() rings = append(rings, ring) eventFds = append(eventFds, event) if err := poller.Add(event.Int(), 0); err != nil { return nil, err } } // Closing a PERF_EVENT_ARRAY removes all event fds // stored in it, so we keep a reference alive. array, err = array.Clone() if err != nil { return nil, err } pr = &Reader{ array: array, rings: rings, poller: poller, deadline: time.Time{}, epollEvents: make([]unix.EpollEvent, len(rings)), epollRings: make([]*perfEventRing, 0, len(rings)), eventHeader: make([]byte, perfEventHeaderSize), eventFds: eventFds, overwritable: opts.Overwritable, bufferSize: bufferSize, } if err = pr.Resume(); err != nil { return nil, err } return pr, nil } // Close frees resources used by the reader. // // It interrupts calls to Read. // // Calls to perf_event_output from eBPF programs will return // ENOENT after calling this method. func (pr *Reader) Close() error { if err := pr.poller.Close(); err != nil { if errors.Is(err, os.ErrClosed) { return nil } return fmt.Errorf("close poller: %w", err) } // Trying to poll will now fail, so Read() can't block anymore. Acquire the // locks so that we can clean up. pr.mu.Lock() defer pr.mu.Unlock() pr.pauseMu.Lock() defer pr.pauseMu.Unlock() for _, ring := range pr.rings { ring.Close() } for _, event := range pr.eventFds { event.Close() } pr.rings = nil pr.eventFds = nil pr.array.Close() return nil } // SetDeadline controls how long Read and ReadInto will block waiting for samples. // // Passing a zero time.Time will remove the deadline. Passing a deadline in the // past will prevent the reader from blocking if there are no records to be read. func (pr *Reader) SetDeadline(t time.Time) { pr.mu.Lock() defer pr.mu.Unlock() pr.deadline = t } // Read the next record from the perf ring buffer. // // The method blocks until there are at least Watermark bytes in one // of the per CPU buffers. Records from buffers below the Watermark // are not returned. // // Records can contain between 0 and 7 bytes of trailing garbage from the ring // depending on the input sample's length. // // Calling [Close] interrupts the method with [os.ErrClosed]. Calling [Flush] // makes it return all records currently in the ring buffer, followed by [ErrFlushed]. // // Returns [os.ErrDeadlineExceeded] if a deadline was set and after all records // have been read from the ring. // // See [Reader.ReadInto] for a more efficient version of this method. func (pr *Reader) Read() (Record, error) { var r Record return r, pr.ReadInto(&r) } var errMustBePaused = fmt.Errorf("perf ringbuffer: must have been paused before reading overwritable buffer") // ReadInto is like [Reader.Read] except that it allows reusing Record and associated buffers. func (pr *Reader) ReadInto(rec *Record) error { pr.mu.Lock() defer pr.mu.Unlock() pr.pauseMu.Lock() defer pr.pauseMu.Unlock() if pr.overwritable && !pr.paused { return errMustBePaused } if pr.rings == nil { return fmt.Errorf("perf ringbuffer: %w", ErrClosed) } for { if len(pr.epollRings) == 0 { if pe := pr.pendingErr; pe != nil { // All rings have been emptied since the error occurred, return // appropriate error. pr.pendingErr = nil return pe } // NB: The deferred pauseMu.Unlock will panic if Wait panics, which // might obscure the original panic. pr.pauseMu.Unlock() _, err := pr.poller.Wait(pr.epollEvents, pr.deadline) pr.pauseMu.Lock() if errors.Is(err, os.ErrDeadlineExceeded) || errors.Is(err, ErrFlushed) { // We've hit the deadline, check whether there is any data in // the rings that we've not been woken up for. pr.pendingErr = err } else if err != nil { return err } // Re-validate pr.paused since we dropped pauseMu. if pr.overwritable && !pr.paused { return errMustBePaused } // Waking up userspace is expensive, make the most of it by checking // all rings. for _, ring := range pr.rings { ring.loadHead() pr.epollRings = append(pr.epollRings, ring) } } // Start at the last available event. The order in which we // process them doesn't matter, and starting at the back allows // resizing epollRings to keep track of processed rings. err := pr.readRecordFromRing(rec, pr.epollRings[len(pr.epollRings)-1]) if err == errEOR { // We've emptied the current ring buffer, process // the next one. pr.epollRings = pr.epollRings[:len(pr.epollRings)-1] continue } return err } } // Pause stops all notifications from this Reader. // // While the Reader is paused, any attempts to write to the event buffer from // BPF programs will return -ENOENT. // // Subsequent calls to Read will block until a call to Resume. func (pr *Reader) Pause() error { pr.pauseMu.Lock() defer pr.pauseMu.Unlock() if pr.eventFds == nil { return fmt.Errorf("%w", ErrClosed) } for i := range pr.eventFds { if err := pr.array.Delete(uint32(i)); err != nil && !errors.Is(err, ebpf.ErrKeyNotExist) { return fmt.Errorf("could't delete event fd for CPU %d: %w", i, err) } } pr.paused = true return nil } // Resume allows this perf reader to emit notifications. // // Subsequent calls to Read will block until the next event notification. func (pr *Reader) Resume() error { pr.pauseMu.Lock() defer pr.pauseMu.Unlock() if pr.eventFds == nil { return fmt.Errorf("%w", ErrClosed) } for i, fd := range pr.eventFds { if fd == nil { continue } if err := pr.array.Put(uint32(i), fd.Uint()); err != nil { return fmt.Errorf("couldn't put event fd %d for CPU %d: %w", fd, i, err) } } pr.paused = false return nil } // BufferSize is the size in bytes of each per-CPU buffer func (pr *Reader) BufferSize() int { return pr.bufferSize } // Flush unblocks Read/ReadInto and successive Read/ReadInto calls will return pending samples at this point, // until you receive a [ErrFlushed] error. func (pr *Reader) Flush() error { return pr.poller.Flush() } // NB: Has to be preceded by a call to ring.loadHead. func (pr *Reader) readRecordFromRing(rec *Record, ring *perfEventRing) error { defer ring.writeTail() rec.CPU = ring.cpu err := readRecord(ring, rec, pr.eventHeader, pr.overwritable) if pr.overwritable && (errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) { return errEOR } rec.Remaining = ring.remaining() return err } type unknownEventError struct { eventType uint32 } func (uev *unknownEventError) Error() string { return fmt.Sprintf("unknown event type: %d", uev.eventType) } // IsUnknownEvent returns true if the error occurred // because an unknown event was submitted to the perf event ring. func IsUnknownEvent(err error) bool { var uee *unknownEventError return errors.As(err, &uee) } golang-github-cilium-ebpf-0.21.0+ds1/perf/reader_test.go000066400000000000000000000452151520243672000227720ustar00rootroot00000000000000//go:build !windows package perf import ( "bytes" "encoding/binary" "errors" "fmt" "math" "os" "syscall" "testing" "time" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/testmain" "github.com/go-quicktest/qt" ) var ( readTimeout = 250 * time.Millisecond ) func TestMain(m *testing.M) { testmain.Run(m) } func TestPerfReader(t *testing.T) { events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() qt.Assert(t, qt.Equals(rd.BufferSize(), 4096)) outputSamples(t, events, 5, 5) _, rem := checkRecord(t, rd) qt.Assert(t, qt.IsTrue(rem >= 5), qt.Commentf("expected at least 5 Remaining")) _, rem = checkRecord(t, rd) qt.Assert(t, qt.Equals(rem, 0), qt.Commentf("expected zero Remaining")) rd.SetDeadline(time.Now().Add(4 * time.Millisecond)) _, err = rd.Read() qt.Assert(t, qt.ErrorIs(err, os.ErrDeadlineExceeded), qt.Commentf("expected os.ErrDeadlineExceeded")) } func TestReaderSetDeadline(t *testing.T) { events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() rd.SetDeadline(time.Now().Add(-time.Second)) if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from first Read, got:", err) } if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from second Read, got:", err) } rd.SetDeadline(time.Now().Add(10 * time.Millisecond)) if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from third Read, got:", err) } } func TestReaderSetDeadlinePendingEvents(t *testing.T) { events := perfEventArray(t) rd, err := NewReaderWithOptions(events, 4096, ReaderOptions{WakeupEvents: 2}) if err != nil { t.Fatal(err) } defer rd.Close() outputSamples(t, events, 5) rd.SetDeadline(time.Now().Add(-time.Second)) _, rem := checkRecord(t, rd) qt.Assert(t, qt.Equals(rem, 0), qt.Commentf("expected zero Remaining")) outputSamples(t, events, 5) // another sample should not be returned before we get ErrFlushed to indicate initial set of samples read _, err = rd.Read() if !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from second Read, got:", err) } // the second sample should now be read _, _ = checkRecord(t, rd) } func TestReaderFlushPendingEvents(t *testing.T) { testutils.LockOSThreadToSingleCPU(t) events := perfEventArray(t) rd, err := NewReaderWithOptions(events, 4096, ReaderOptions{WakeupEvents: 2}) if err != nil { t.Fatal(err) } defer rd.Close() outputSamples(t, events, 5) wait := make(chan int) go func() { wait <- 0 _, rem := checkRecord(t, rd) wait <- rem }() <-wait time.Sleep(10 * time.Millisecond) err = rd.Flush() qt.Assert(t, qt.IsNil(err)) rem := <-wait qt.Assert(t, qt.Equals(rem, 0), qt.Commentf("expected zero Remaining")) outputSamples(t, events, 5) // another sample should not be returned before we get ErrFlushed to indicate initial set of samples read _, err = rd.Read() if !errors.Is(err, ErrFlushed) { t.Error("Expected ErrFlushed from second Read, got:", err) } // the second sample should now be read _, _ = checkRecord(t, rd) } func outputSamples(tb testing.TB, events *ebpf.Map, sampleSizes ...byte) { prog := outputSamplesProg(tb, events, sampleSizes...) ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(tb, err) if err != nil { tb.Fatal(err) } if errno := syscall.Errno(-int32(ret)); errno != 0 { tb.Fatal("Expected 0 as return value, got", errno) } } // outputSamplesProg creates a program which submits a series of samples to a PerfEventArray. // // The format of each sample is: // // index: 0 1 2 3 ... size - 1 // content: size id 0xff 0xff ... 0xff [padding] // // padding is an implementation detail of the perf buffer and 1-7 bytes long. The // contents are undefined. func outputSamplesProg(tb testing.TB, events *ebpf.Map, sampleSizes ...byte) *ebpf.Program { tb.Helper() // Requires at least 4.9 (0515e5999a46 "bpf: introduce BPF_PROG_TYPE_PERF_EVENT program type") testutils.SkipOnOldKernel(tb, "4.9", "perf events support") const bpfFCurrentCPU = 0xffffffff var maxSampleSize byte for _, sampleSize := range sampleSizes { if sampleSize < 2 { tb.Fatalf("Sample size %d is too small to contain size and counter", sampleSize) } if sampleSize > maxSampleSize { maxSampleSize = sampleSize } } // Fill a buffer on the stack, and stash context somewhere insns := asm.Instructions{ asm.LoadImm(asm.R0, ^int64(0), asm.DWord), asm.Mov.Reg(asm.R9, asm.R1), } bufDwords := int(maxSampleSize/8) + 1 for i := 0; i < bufDwords; i++ { insns = append(insns, asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord), ) } for i, sampleSize := range sampleSizes { insns = append(insns, // Restore stashed context. asm.Mov.Reg(asm.R1, asm.R9), // map asm.LoadMapPtr(asm.R2, events.FD()), // flags asm.LoadImm(asm.R3, bpfFCurrentCPU, asm.DWord), // buffer asm.Mov.Reg(asm.R4, asm.RFP), asm.Add.Imm(asm.R4, int32(bufDwords*-8)), // buffer[0] = size asm.StoreImm(asm.R4, 0, int64(sampleSize), asm.Byte), // buffer[1] = i asm.StoreImm(asm.R4, 1, int64(i&math.MaxUint8), asm.Byte), // size asm.Mov.Imm(asm.R5, int32(sampleSize)), asm.FnPerfEventOutput.Call(), ) } insns = append(insns, asm.Return()) prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ License: "GPL", Type: ebpf.XDP, Instructions: insns, }) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { prog.Close() }) return prog } func checkRecord(tb testing.TB, rd *Reader) (id int, remaining int) { tb.Helper() rec, err := rd.Read() qt.Assert(tb, qt.IsNil(err)) qt.Assert(tb, qt.IsTrue(rec.CPU >= 0), qt.Commentf("Record has invalid CPU number")) size := int(rec.RawSample[0]) qt.Assert(tb, qt.IsTrue(len(rec.RawSample) >= size), qt.Commentf("RawSample is at least size bytes")) for i, v := range rec.RawSample[2:size] { qt.Assert(tb, qt.Equals(v, 0xff), qt.Commentf("filler at position %d should match", i+2)) } // padding is ignored since it's value is undefined. return int(rec.RawSample[1]), rec.Remaining } func TestPerfReaderLostSample(t *testing.T) { // To generate a lost sample perf record: // // 1. Fill the perf ring buffer almost completely, with the output_large program. // The buffer is sized in number of pages, which are architecture dependant. // // 2. Write an extra event that doesn't fit in the space remaining. // // 3. Write a smaller event that does fit, with output_single program. // Lost sample records are generated opportunistically, when the kernel // is writing an event and realizes that there were events lost previously. // // The event size is hardcoded in the test BPF programs, there's no way // to parametrize it without rebuilding the programs. // // The event size needs to be selected so that, for any page size, there are at least // 48 bytes left in the perf ring page after filling it with a whole number of events: // // - PERF_RECORD_LOST: 8 (perf_event_header) + 16 (PERF_RECORD_LOST) // // - output_single: 8 (perf_event_header) + 4 (size) + 5 (payload) + 7 (padding to 64bits) // // By selecting an event size of the form 2^n + 2^(n+1), for any page size 2^(n+m), m >= 0, // the number of bytes left, x, after filling a page with a whole number of events is: // // 2^(n+m) 2^n * 2^m // x = 2^n * frac(---------------) <=> x = 2^n * frac(---------------) // 2^n + 2^(n+1) 2^n + 2^n * 2 // // 2^n * 2^m // <=> x = 2^n * frac(---------------) // 2^n * (1 + 2) // // 2^m // <=> x = 2^n * frac(-----) // 3 // // 1 2 // <=> x = 2^n * - or x = 2^n * - // 3 3 // // Selecting n = 6, we have: // // x = 64 or x = 128, no matter the page size 2^(6+m) // // event size = 2^6 + 2^7 = 192 // // Accounting for perf headers, output_large uses a 180 byte payload: // // 8 (perf_event_header) + 4 (size) + 180 (payload) const ( eventSize = 192 ) var ( pageSize = os.Getpagesize() maxEvents = (pageSize / eventSize) ) if remainder := pageSize % eventSize; remainder != 64 && remainder != 128 { // Page size isn't 2^(6+m), m >= 0 t.Fatal("unsupported page size:", pageSize) } var sampleSizes []byte // Fill the ring with the maximum number of output_large events that will fit, // and generate a lost event by writing an additional event. for i := 0; i < maxEvents+1; i++ { sampleSizes = append(sampleSizes, 180) } // Generate a small event to trigger the lost record sampleSizes = append(sampleSizes, 5) events := perfEventArray(t) rd, err := NewReader(events, pageSize) if err != nil { t.Fatal(err) } defer rd.Close() outputSamples(t, events, sampleSizes...) for range sampleSizes { record, err := rd.Read() if err != nil { t.Fatal(err) } if record.RawSample == nil && record.LostSamples != 1 { t.Fatal("Expected a record with LostSamples 1, got", record.LostSamples) } } } func TestPerfReaderOverwritable(t *testing.T) { // Smallest buffer size. pageSize := os.Getpagesize() const sampleSize = math.MaxUint8 // Account for perf header (8) and size (4), align to 8 bytes as perf does. realSampleSize := internal.Align(sampleSize+8+4, 8) maxEvents := pageSize / realSampleSize var sampleSizes []byte for i := 0; i < maxEvents; i++ { sampleSizes = append(sampleSizes, sampleSize) } // Append an extra sample that will overwrite the first sample. sampleSizes = append(sampleSizes, sampleSize) events := perfEventArray(t) rd, err := NewReaderWithOptions(events, pageSize, ReaderOptions{Overwritable: true}) if err != nil { t.Fatal(err) } defer rd.Close() _, err = rd.Read() qt.Assert(t, qt.ErrorIs(err, errMustBePaused)) outputSamples(t, events, sampleSizes...) qt.Assert(t, qt.IsNil(rd.Pause())) rd.SetDeadline(time.Now()) nextID := maxEvents for i := 0; i < maxEvents; i++ { id, rem := checkRecord(t, rd) qt.Assert(t, qt.Equals(id, nextID)) qt.Assert(t, qt.Equals(rem, -1)) nextID-- } } func TestPerfReaderOverwritableEmpty(t *testing.T) { events := perfEventArray(t) rd, err := NewReaderWithOptions(events, os.Getpagesize(), ReaderOptions{Overwritable: true}) if err != nil { t.Fatal(err) } defer rd.Close() err = rd.Pause() if err != nil { t.Fatal(err) } rd.SetDeadline(time.Now().Add(4 * time.Millisecond)) _, err = rd.Read() qt.Assert(t, qt.ErrorIs(err, os.ErrDeadlineExceeded), qt.Commentf("expected os.ErrDeadlineExceeded")) err = rd.Resume() if err != nil { t.Fatal(err) } } func TestPerfReaderClose(t *testing.T) { events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() errs := make(chan error, 1) waiting := make(chan struct{}) go func() { close(waiting) _, err := rd.Read() errs <- err }() <-waiting // Close should interrupt Read if err := rd.Close(); err != nil { t.Fatal(err) } select { case <-errs: case <-time.After(time.Second): t.Fatal("Close doesn't interrupt Read") } // And we should be able to call it multiple times if err := rd.Close(); err != nil { t.Fatal(err) } if _, err := rd.Read(); err == nil { t.Fatal("Read on a closed PerfReader doesn't return an error") } } func TestCreatePerfEvent(t *testing.T) { fd, err := createPerfEvent(0, ReaderOptions{Watermark: 1, Overwritable: false}) if err != nil { t.Fatal("Can't create perf event:", err) } fd.Close() } func TestReadRecord(t *testing.T) { var buf bytes.Buffer err := binary.Write(&buf, internal.NativeEndian, &perfEventHeader{}) if err != nil { t.Fatal(err) } var rec Record err = readRecord(&buf, &rec, make([]byte, perfEventHeaderSize), false) if !IsUnknownEvent(err) { t.Error("readRecord should return unknown event error, got", err) } } func TestPause(t *testing.T) { t.Parallel() events := perfEventArray(t) rd, err := NewReader(events, 4096) if err != nil { t.Fatal(err) } defer rd.Close() // Reader is already unpaused by default. It should be idempotent. if err = rd.Resume(); err != nil { t.Fatal(err) } // Write a sample. The reader should read it. prog := outputSamplesProg(t, events, 5) ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) if err != nil || ret != 0 { t.Fatal("Can't write sample") } if _, err := rd.Read(); err != nil { t.Fatal(err) } // Pause. No notification should trigger. if err = rd.Pause(); err != nil { t.Fatal(err) } errChan := make(chan error, 1) go func() { // Read one notification then send any errors and exit. _, err := rd.Read() errChan <- err }() ret, _, err = prog.Test(internal.EmptyBPFContext) if err == nil && ret == 0 { t.Fatal("Unexpectedly wrote sample while paused") } // else Success select { case err := <-errChan: // Failure: Pause was unsuccessful. t.Fatalf("received notification on paused reader: %s", err) case <-time.After(readTimeout): // Success } // Pause should be idempotent. if err = rd.Pause(); err != nil { t.Fatal(err) } // Resume. Now notifications should continue. if err = rd.Resume(); err != nil { t.Fatal(err) } ret, _, err = prog.Test(internal.EmptyBPFContext) if err != nil || ret != 0 { t.Fatal("Can't write sample") } select { case err := <-errChan: if err != nil { t.Fatal(err) } // else Success case <-time.After(readTimeout): t.Fatal("timed out waiting for notification after resume") } if err = rd.Close(); err != nil { t.Fatal(err) } // Pause/Resume after close should be no-op. err = rd.Pause() qt.Assert(t, qt.Not(qt.Equals(err, ErrClosed)), qt.Commentf("returns unwrapped ErrClosed")) qt.Assert(t, qt.ErrorIs(err, ErrClosed), qt.Commentf("doesn't wrap ErrClosed")) err = rd.Resume() qt.Assert(t, qt.Not(qt.Equals(err, ErrClosed)), qt.Commentf("returns unwrapped ErrClosed")) qt.Assert(t, qt.ErrorIs(err, ErrClosed), qt.Commentf("doesn't wrap ErrClosed")) } func TestPerfReaderWakeupEvents(t *testing.T) { testutils.LockOSThreadToSingleCPU(t) events := perfEventArray(t) numEvents := 2 rd, err := NewReaderWithOptions(events, 4096, ReaderOptions{WakeupEvents: numEvents}) if err != nil { t.Fatal(err) } defer rd.Close() prog := outputSamplesProg(t, events, 5) // Send enough events to trigger WakeupEvents. for i := 0; i < numEvents; i++ { _, _, err = prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) } time.AfterFunc(5*time.Second, func() { // Interrupt Read() in case the implementation is buggy. rd.Close() }) for i := 0; i < numEvents; i++ { checkRecord(t, rd) } } func TestReadWithoutWakeup(t *testing.T) { t.Parallel() events := perfEventArray(t) rd, err := NewReaderWithOptions(events, 1, ReaderOptions{WakeupEvents: 2}) if err != nil { t.Fatal(err) } defer rd.Close() prog := outputSamplesProg(t, events, 5) ret, _, err := prog.Test(internal.EmptyBPFContext) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(ret, 0)) rd.SetDeadline(time.Now()) checkRecord(t, rd) } func BenchmarkReader(b *testing.B) { events := perfEventArray(b) prog := outputSamplesProg(b, events, 80) rd, err := NewReader(events, 4096) if err != nil { b.Fatal(err) } defer rd.Close() buf := internal.EmptyBPFContext b.ReportAllocs() for b.Loop() { ret, _, err := prog.Test(buf) if err != nil { b.Fatal(err) } else if errno := syscall.Errno(-int32(ret)); errno != 0 { b.Fatal("Expected 0 as return value, got", errno) } if _, err = rd.Read(); err != nil { b.Fatal(err) } } } func BenchmarkReadInto(b *testing.B) { events := perfEventArray(b) prog := outputSamplesProg(b, events, 80) rd, err := NewReader(events, 4096) if err != nil { b.Fatal(err) } defer rd.Close() buf := internal.EmptyBPFContext b.ReportAllocs() var rec Record for b.Loop() { // NB: Submitting samples into the perf event ring dominates // the benchmark time unfortunately. ret, _, err := prog.Test(buf) if err != nil { b.Fatal(err) } else if errno := syscall.Errno(-int32(ret)); errno != 0 { b.Fatal("Expected 0 as return value, got", errno) } if err := rd.ReadInto(&rec); err != nil { b.Fatal(err) } } } // This exists just to make the example below nicer. func bpfPerfEventOutputProgram() (*ebpf.Program, *ebpf.Map) { return nil, nil } // ExampleReader submits a perf event using BPF, // and then reads it in user space. // // The BPF will look something like this: // // struct map events __section("maps") = { // .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, // }; // // __section("xdp") int output_single(void *ctx) { // unsigned char buf[] = { // 1, 2, 3, 4, 5 // }; // // return perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &buf[0], 5); // } // // Also see BPF_F_CTXLEN_MASK if you want to sample packet data // from SKB or XDP programs. func ExampleReader() { prog, events := bpfPerfEventOutputProgram() defer prog.Close() defer events.Close() rd, err := NewReader(events, 4096) if err != nil { panic(err) } defer rd.Close() // Writes out a sample with content 1,2,3,4,4 ret, _, err := prog.Test(internal.EmptyBPFContext) if err != nil || ret != 0 { panic("Can't write sample") } record, err := rd.Read() if err != nil { panic(err) } // Data is padded with 0 for alignment fmt.Println("Sample:", record.RawSample) } // ReadRecord allows reducing memory allocations. func ExampleReader_ReadInto() { prog, events := bpfPerfEventOutputProgram() defer prog.Close() defer events.Close() rd, err := NewReader(events, 4096) if err != nil { panic(err) } defer rd.Close() for i := 0; i < 2; i++ { // Write out two samples ret, _, err := prog.Test(internal.EmptyBPFContext) if err != nil || ret != 0 { panic("Can't write sample") } } var rec Record for i := 0; i < 2; i++ { if err := rd.ReadInto(&rec); err != nil { panic(err) } fmt.Println("Sample:", rec.RawSample[:5]) } } func perfEventArray(tb testing.TB) *ebpf.Map { events, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.PerfEventArray, }) if err != nil { tb.Fatal(err) } tb.Cleanup(func() { events.Close() }) return events } golang-github-cilium-ebpf-0.21.0+ds1/perf/ring.go000066400000000000000000000160141520243672000214230ustar00rootroot00000000000000//go:build !windows package perf import ( "errors" "fmt" "io" "math" "os" "runtime" "sync/atomic" "unsafe" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) // perfEventRing is a page of metadata followed by // a variable number of pages which form a ring buffer. type perfEventRing struct { cpu int mmap []byte ringReader cleanup runtime.Cleanup } func newPerfEventRing(cpu, perCPUBuffer int, opts ReaderOptions) (_ *sys.FD, _ *perfEventRing, err error) { closeOnError := func(c io.Closer) { if err != nil { c.Close() } } if opts.Watermark >= perCPUBuffer { return nil, nil, errors.New("watermark must be smaller than perCPUBuffer") } fd, err := createPerfEvent(cpu, opts) if err != nil { return nil, nil, err } defer closeOnError(fd) if err := unix.SetNonblock(fd.Int(), true); err != nil { return nil, nil, err } protections := unix.PROT_READ if !opts.Overwritable { protections |= unix.PROT_WRITE } mmap, err := unix.Mmap(fd.Int(), 0, perfBufferSize(perCPUBuffer), protections, unix.MAP_SHARED) if err != nil { return nil, nil, fmt.Errorf("can't mmap: %v", err) } // This relies on the fact that we allocate an extra metadata page, // and that the struct is smaller than an OS page. // This use of unsafe.Pointer isn't explicitly sanctioned by the // documentation, since a byte is smaller than sampledPerfEvent. meta := (*unix.PerfEventMmapPage)(unsafe.Pointer(&mmap[0])) var reader ringReader if opts.Overwritable { reader = newReverseReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size]) } else { reader = newForwardReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size]) } ring := &perfEventRing{ cpu: cpu, mmap: mmap, ringReader: reader, } ring.cleanup = runtime.AddCleanup(ring, func(mmap []byte) { _ = unix.Munmap(mmap) }, ring.mmap) return fd, ring, nil } // perfBufferSize returns a valid mmap buffer size for use with perf_event_open (1+2^n pages) func perfBufferSize(perCPUBuffer int) int { pageSize := os.Getpagesize() // Smallest whole number of pages nPages := (perCPUBuffer + pageSize - 1) / pageSize // Round up to nearest power of two number of pages nPages = int(math.Pow(2, math.Ceil(math.Log2(float64(nPages))))) // Add one for metadata nPages += 1 return nPages * pageSize } func (ring *perfEventRing) Close() error { ring.cleanup.Stop() mmap := ring.mmap ring.mmap = nil return unix.Munmap(mmap) } func createPerfEvent(cpu int, opts ReaderOptions) (*sys.FD, error) { wakeup := 0 bits := 0 if opts.WakeupEvents > 0 { wakeup = opts.WakeupEvents } else { wakeup = opts.Watermark if wakeup == 0 { wakeup = 1 } bits |= unix.PerfBitWatermark } if opts.Overwritable { bits |= unix.PerfBitWriteBackward } attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_SOFTWARE, Config: unix.PERF_COUNT_SW_BPF_OUTPUT, Bits: uint64(bits), Sample_type: unix.PERF_SAMPLE_RAW, Wakeup: uint32(wakeup), } attr.Size = uint32(unsafe.Sizeof(attr)) fd, err := unix.PerfEventOpen(&attr, -1, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC) if err != nil { return nil, fmt.Errorf("can't create perf event: %w", err) } return sys.NewFD(fd) } type ringReader interface { loadHead() size() int remaining() int writeTail() Read(p []byte) (int, error) } type forwardReader struct { meta *unix.PerfEventMmapPage head, tail uint64 mask uint64 ring []byte } func newForwardReader(meta *unix.PerfEventMmapPage, ring []byte) *forwardReader { return &forwardReader{ meta: meta, head: atomic.LoadUint64(&meta.Data_head), tail: atomic.LoadUint64(&meta.Data_tail), // cap is always a power of two mask: uint64(cap(ring) - 1), ring: ring, } } func (rr *forwardReader) loadHead() { rr.head = atomic.LoadUint64(&rr.meta.Data_head) } func (rr *forwardReader) size() int { return len(rr.ring) } func (rr *forwardReader) remaining() int { return int((rr.head - rr.tail) & rr.mask) } func (rr *forwardReader) writeTail() { // Commit the new tail. This lets the kernel know that // the ring buffer has been consumed. atomic.StoreUint64(&rr.meta.Data_tail, rr.tail) } func (rr *forwardReader) Read(p []byte) (int, error) { start := int(rr.tail & rr.mask) n := len(p) // Truncate if the read wraps in the ring buffer if remainder := cap(rr.ring) - start; n > remainder { n = remainder } // Truncate if there isn't enough data if remainder := int(rr.head - rr.tail); n > remainder { n = remainder } copy(p, rr.ring[start:start+n]) rr.tail += uint64(n) if rr.tail == rr.head { return n, io.EOF } return n, nil } type reverseReader struct { meta *unix.PerfEventMmapPage // head is the position where the kernel last wrote data. head uint64 // read is the position we read the next data from. Updated as reads are made. read uint64 // tail is the end of the ring buffer. No reads must be made past it. tail uint64 mask uint64 ring []byte } func newReverseReader(meta *unix.PerfEventMmapPage, ring []byte) *reverseReader { rr := &reverseReader{ meta: meta, mask: uint64(cap(ring) - 1), ring: ring, } rr.loadHead() return rr } func (rr *reverseReader) loadHead() { // The diagram below represents an overwritable perf ring buffer: // // head read tail // | | | // V V V // +---+--------+------------+---------+--------+ // | |H-D....D|H-C........C|H-B.....B|H-A....A| // +---+--------+------------+---------+--------+ // <--Write from right to left // Read from left to right--> // (H means header) // // The buffer is read left to right beginning from head to tail. // [head, read) is the read portion of the buffer, [read, tail) the unread one. // read is adjusted as we progress through the buffer. // Avoid reading sample D multiple times by discarding unread samples C, B, A. rr.tail = rr.head // Get the new head and starting reading from it. rr.head = atomic.LoadUint64(&rr.meta.Data_head) rr.read = rr.head if rr.tail-rr.head > uint64(cap(rr.ring)) { // ring has been fully written, only permit at most cap(rr.ring) // bytes to be read. rr.tail = rr.head + uint64(cap(rr.ring)) } } func (rr *reverseReader) size() int { return len(rr.ring) } func (rr *reverseReader) remaining() int { // remaining data is inaccurate for overwritable buffers // once an overwrite happens, so return -1 here. return -1 } func (rr *reverseReader) writeTail() { // We do not care about tail for over writable perf buffer. // So, this function is noop. } func (rr *reverseReader) Read(p []byte) (int, error) { start := int(rr.read & rr.mask) n := len(p) // Truncate if the read wraps in the ring buffer if remainder := cap(rr.ring) - start; n > remainder { n = remainder } // Truncate if there isn't enough data if remainder := int(rr.tail - rr.read); n > remainder { n = remainder } copy(p, rr.ring[start:start+n]) rr.read += uint64(n) if rr.read == rr.tail { return n, io.EOF } return n, nil } golang-github-cilium-ebpf-0.21.0+ds1/perf/ring_test.go000066400000000000000000000114631520243672000224650ustar00rootroot00000000000000//go:build !windows package perf import ( "io" "os" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/unix" ) func TestRingBufferReader(t *testing.T) { ring := makeForwardRing(2, 0) checkRead(t, ring, []byte{0, 1}, io.EOF) checkRead(t, ring, []byte{}, io.EOF) // Wrapping read ring = makeForwardRing(2, 1) checkRead(t, ring, []byte{1}, nil) checkRead(t, ring, []byte{0}, io.EOF) checkRead(t, ring, []byte{}, io.EOF) } func TestRingBufferReverseReader(t *testing.T) { // First case: read 4, starting from offset 2. // The buffer should contain the following: // // [0 1 2 3] // ^ // | // head // // As we read from position 2, we should get [2, 3]. // Then, when we read it for the second time, we should get [0, 1] as we would // have looped around the buffer. ring := makeReverseRing(4, 2) checkRead(t, ring, []byte{2, 3}, nil) checkRead(t, ring, []byte{0, 1}, io.EOF) checkRead(t, ring, []byte{}, io.EOF) // Complicated case: read bytes until previous_head. // // [0 1 2 3] // ^ ^ // | | // | +---previous_head // head ring = makeReverseRing(4, 2) checkReadBuffer(t, ring, []byte{2}, nil, make([]byte, 1)) // Next read would be {3}, but we don't consume it. // Pretend the kernel wrote another 2 bytes. ring.meta.Data_head -= 2 ring.loadHead() // {3} is discarded. checkRead(t, ring, []byte{0, 1}, io.EOF) // Complicated case: read the whole buffer because it was "overwritten". // // [0 1 2 3] // ^ // | // +---previous_head // | // head // // So, we should first read [2, 3] then [0, 1]. ring = makeReverseRing(4, 2) ring.meta.Data_head -= ring.meta.Data_size ring.loadHead() checkRead(t, ring, []byte{2, 3}, nil) checkRead(t, ring, []byte{0, 1}, io.EOF) } // ensure that the next call to Read() yields the correct result. // // Read is called with a buffer that is larger than want so // that corner cases around wrapping can be checked. Use // checkReadBuffer if that is not desired. func checkRead(t *testing.T, r io.Reader, want []byte, wantErr error) { checkReadBuffer(t, r, want, wantErr, make([]byte, len(want)+1)) } func checkReadBuffer(t *testing.T, r io.Reader, want []byte, wantErr error, buf []byte) { t.Helper() n, err := r.Read(buf) buf = buf[:n] qt.Assert(t, qt.Equals(err, wantErr)) qt.Assert(t, qt.DeepEquals(buf, want)) } func makeBuffer(size int) []byte { buf := make([]byte, size) for i := range buf { buf[i] = byte(i) } return buf } func makeReverseRing(size, offset int) *reverseReader { if size != 0 && (size&(size-1)) != 0 { panic("size must be power of two") } meta := unix.PerfEventMmapPage{ Data_head: 0 - uint64(size) - uint64(offset), Data_tail: 0, // never written by the kernel Data_size: uint64(size), } return newReverseReader(&meta, makeBuffer(size)) } func makeForwardRing(size, offset int) *forwardReader { if size != 0 && (size&(size-1)) != 0 { panic("size must be power of two") } meta := unix.PerfEventMmapPage{ Data_head: uint64(size + offset), Data_tail: uint64(offset), Data_size: uint64(size), } return newForwardReader(&meta, makeBuffer(size)) } func TestPerfEventRing(t *testing.T) { check := func(buffer, watermark int, overwritable bool) { event, ring, err := newPerfEventRing(0, buffer, ReaderOptions{Watermark: watermark, Overwritable: overwritable}) if err != nil { t.Fatal(err) } defer event.Close() defer ring.Close() size := ring.size() // Ring size should be at least as big as buffer if size < buffer { t.Fatalf("ring size %d smaller than buffer %d", size, buffer) } // Ring size should be of the form 2^n pages (meta page has already been removed) if size%os.Getpagesize() != 0 { t.Fatalf("ring size %d not whole number of pages (pageSize %d)", size, os.Getpagesize()) } nPages := size / os.Getpagesize() if nPages&(nPages-1) != 0 { t.Fatalf("ring size %d (%d pages) not a power of two pages (pageSize %d)", size, nPages, os.Getpagesize()) } } // watermark > buffer _, _, err := newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: false}) if err == nil { t.Fatal("watermark > buffer allowed") } _, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true}) if err == nil { t.Fatal("watermark > buffer allowed") } // watermark == buffer _, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8192, Overwritable: false}) if err == nil { t.Fatal("watermark == buffer allowed") } _, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true}) if err == nil { t.Fatal("watermark == buffer allowed") } // buffer not a power of two, watermark < buffer check(8193, 8192, false) check(8193, 8192, true) // large buffer not a multiple of page size at all (prime) check(65537, 8192, false) check(65537, 8192, true) } golang-github-cilium-ebpf-0.21.0+ds1/pin/000077500000000000000000000000001520243672000177655ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/pin/doc.go000066400000000000000000000001411520243672000210550ustar00rootroot00000000000000// Package pin provides utility functions for working with pinned objects on bpffs. package pin golang-github-cilium-ebpf-0.21.0+ds1/pin/load.go000066400000000000000000000020211520243672000212260ustar00rootroot00000000000000package pin import ( "fmt" "io" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/link" ) // Pinner is an interface implemented by all eBPF objects that support pinning // to a bpf virtual filesystem. type Pinner interface { io.Closer Pin(string) error } // Load retrieves a pinned object from a bpf virtual filesystem. It returns one // of [ebpf.Map], [ebpf.Program], or [link.Link]. // // Trying to open anything other than a bpf object is an error. func Load(path string, opts *ebpf.LoadPinOptions) (Pinner, error) { fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(path), FileFlags: opts.Marshal(), }) if err != nil { return nil, fmt.Errorf("opening pin %s: %w", path, err) } switch typ { case sys.BPF_TYPE_MAP: return ebpf.NewMapFromFD(fd.Disown()) case sys.BPF_TYPE_PROG: return ebpf.NewProgramFromFD(fd.Disown()) case sys.BPF_TYPE_LINK: return link.NewFromFD(fd.Disown()) } return nil, fmt.Errorf("unknown object type %d", typ) } golang-github-cilium-ebpf-0.21.0+ds1/pin/load_test.go000066400000000000000000000035111520243672000222720ustar00rootroot00000000000000package pin import ( "path/filepath" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/testmain" ) func mustPinnedProgram(t *testing.T, path string) *ebpf.Program { t.Helper() typ := ebpf.SocketFilter if platform.IsWindows { typ = ebpf.WindowsSample } spec := &ebpf.ProgramSpec{ Name: "test", Type: typ, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 2, asm.DWord), asm.Return(), }, License: "MIT", } p, err := ebpf.NewProgram(spec) if err != nil { t.Fatal(err) } t.Cleanup(func() { p.Close() }) if err := p.Pin(path); err != nil { t.Fatal(err) } return p } func mustPinnedMap(t *testing.T, path string) *ebpf.Map { t.Helper() typ := ebpf.Array if platform.IsWindows { typ = ebpf.WindowsArray } spec := &ebpf.MapSpec{ Name: "test", Type: typ, KeySize: 4, ValueSize: 4, MaxEntries: 1, } m, err := ebpf.NewMap(spec) if err != nil { t.Fatal(err) } t.Cleanup(func() { m.Close() }) if err := m.Pin(path); err != nil { t.Fatal(err) } return m } func TestLoad(t *testing.T) { testutils.SkipOnOldKernel(t, "4.10", "reading program fdinfo") tmp := testutils.TempBPFFS(t) mpath := filepath.Join(tmp, "map") ppath := filepath.Join(tmp, "prog") mustPinnedMap(t, mpath) mustPinnedProgram(t, ppath) _, err := Load(tmp, nil) qt.Assert(t, qt.IsNotNil(err)) m, err := Load(mpath, nil) qt.Assert(t, qt.IsNil(err)) defer m.Close() qt.Assert(t, qt.Satisfies(m, testutils.Contains[*ebpf.Map])) p, err := Load(ppath, nil) qt.Assert(t, qt.IsNil(err)) defer p.Close() qt.Assert(t, qt.Satisfies(p, testutils.Contains[*ebpf.Program])) } func TestMain(m *testing.M) { testmain.Run(m) } golang-github-cilium-ebpf-0.21.0+ds1/pin/pin.go000066400000000000000000000005251520243672000211040ustar00rootroot00000000000000package pin import "io" // Pin represents an object and its pinned path. type Pin struct { Path string Object io.Closer } func (p *Pin) close() { if p.Object != nil { p.Object.Close() } } // Take ownership of Pin.Object. // // The caller is responsible for calling close on [Pin.Object]. func (p *Pin) Take() { p.Object = nil } golang-github-cilium-ebpf-0.21.0+ds1/pin/walk_other.go000066400000000000000000000022731520243672000224570ustar00rootroot00000000000000//go:build !windows package pin import ( "fmt" "io/fs" "iter" "os" "path/filepath" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/unix" ) // WalkDir walks the file tree rooted at path and yields a [Pin] for each // BPF object below the path. // // Callers must invoke [Pin.Take] if they wish to hold on to the object. func WalkDir(root string, opts *ebpf.LoadPinOptions) iter.Seq2[*Pin, error] { return func(yield func(*Pin, error) bool) { fsType, err := linux.FSType(root) if err != nil { yield(nil, err) return } if fsType != unix.BPF_FS_MAGIC { yield(nil, fmt.Errorf("%s is not on a bpf filesystem", root)) return } fn := func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } path = filepath.Join(root, path) obj, err := Load(path, opts) if err != nil { return err } pin := &Pin{Path: path, Object: obj} defer pin.close() if !yield(pin, nil) { return fs.SkipAll } return nil } err = fs.WalkDir(os.DirFS(root), ".", fn) if err != nil { yield(nil, fmt.Errorf("walk: %w", err)) return } } } golang-github-cilium-ebpf-0.21.0+ds1/pin/walk_test.go000066400000000000000000000030021520243672000223040ustar00rootroot00000000000000package pin import ( "iter" "os" "path/filepath" "reflect" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/testutils" ) func TestWalkDir(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "reading program objinfo") tmp := testutils.TempBPFFS(t) dir := filepath.Join(tmp, "dir") if !platform.IsWindows { // Windows doesn't have a BPF file system, so mkdir below fails. qt.Assert(t, qt.IsNil(os.Mkdir(dir, 0755))) } progPath := filepath.Join(tmp, "pinned_prog") mustPinnedProgram(t, progPath) mapPath := filepath.Join(dir, "pinned_map") mustPinnedMap(t, mapPath) next, stop := iter.Pull2(WalkDir(tmp, nil)) defer stop() pin, err, ok := next() qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(reflect.TypeOf(pin.Object), reflect.TypeFor[*ebpf.Map]())) qt.Assert(t, qt.Equals(pin.Path, mapPath)) pin, err, ok = next() qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(reflect.TypeOf(pin.Object), reflect.TypeFor[*ebpf.Program]())) qt.Assert(t, qt.Equals(pin.Path, progPath)) _, _, ok = next() qt.Assert(t, qt.IsFalse(ok)) t.Run("Not BPFFS", func(t *testing.T) { if platform.IsWindows { t.Skip("Windows does not have BPFFS") } next, stop := iter.Pull2(WalkDir("/", nil)) defer stop() _, err, ok = next() qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsNotNil(err)) _, _, ok = next() qt.Assert(t, qt.IsFalse(ok)) }) } golang-github-cilium-ebpf-0.21.0+ds1/pin/walk_windows.go000066400000000000000000000020731520243672000230260ustar00rootroot00000000000000package pin import ( "errors" "fmt" "iter" "strings" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/efw" ) // WalkDir walks the file tree rooted at path and yields a [Pin] for each // BPF object below the path. // // Callers must invoke [Pin.Take] if they wish to hold on to the object. func WalkDir(root string, opts *ebpf.LoadPinOptions) iter.Seq2[*Pin, error] { return func(yield func(*Pin, error) bool) { root, err := efw.EbpfCanonicalizePinPath(root) if err != nil { yield(nil, fmt.Errorf("failed to canonicalize pin path %q: %w", root, err)) return } cursor := root for { next, _, err := efw.EbpfGetNextPinnedObjectPath(cursor, efw.EBPF_OBJECT_UNKNOWN) if errors.Is(err, efw.EBPF_NO_MORE_KEYS) { break } else if err != nil { yield(nil, err) return } if !strings.HasPrefix(next, root) { break } obj, err := Load(next, opts) if err != nil { yield(nil, err) return } pin := &Pin{next, obj} ok := yield(pin, nil) pin.close() if !ok { return } cursor = next } } } golang-github-cilium-ebpf-0.21.0+ds1/prog.go000066400000000000000000001134041520243672000205000ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "fmt" "math" "path/filepath" "runtime" "slices" "strings" "time" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/sysenc" "github.com/cilium/ebpf/internal/unix" ) // ErrNotSupported is returned whenever the kernel doesn't support a feature. var ErrNotSupported = internal.ErrNotSupported // ErrProgIncompatible is returned when a loaded Program is incompatible with a // given spec. var ErrProgIncompatible = errors.New("program is incompatible") // errBadRelocation is returned when the verifier rejects a program due to a // bad CO-RE relocation. // // This error is detected based on heuristics and therefore may not be reliable. var errBadRelocation = errors.New("bad CO-RE relocation") // errUnknownKfunc is returned when the verifier rejects a program due to an // unknown kfunc. // // This error is detected based on heuristics and therefore may not be reliable. var errUnknownKfunc = errors.New("unknown kfunc") // ProgramID represents the unique ID of an eBPF program. type ProgramID = sys.ProgramID const ( // Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN. // This is currently the maximum of spare space allocated for SKB // and XDP programs, and equal to XDP_PACKET_HEADROOM + NET_IP_ALIGN. outputPad = 256 + 2 ) // minVerifierLogSize is the default number of bytes allocated for the // verifier log. const minVerifierLogSize = 64 * 1024 // maxVerifierLogSize is the maximum size of verifier log buffer the kernel // will accept before returning EINVAL. May be increased to MaxUint32 in the // future, but avoid the unnecessary EINVAL for now. const maxVerifierLogSize = math.MaxUint32 >> 2 // maxVerifierAttempts is the maximum number of times the verifier will retry // loading a program with a growing log buffer before giving up. Since we double // the log size on every attempt, this is the absolute maximum number of // attempts before the buffer reaches [maxVerifierLogSize]. const maxVerifierAttempts = 30 // ProgramOptions control loading a program into the kernel. type ProgramOptions struct { // Bitmap controlling the detail emitted by the kernel's eBPF verifier log. // LogLevel-type values can be ORed together to request specific kinds of // verifier output. See the documentation on [ebpf.LogLevel] for details. // // opts.LogLevel = (ebpf.LogLevelBranch | ebpf.LogLevelStats) // // If left to its default value, the program will first be loaded without // verifier output enabled. Upon error, the program load will be repeated // with LogLevelBranch and the given (or default) LogSize value. // // Unless LogDisabled is set, setting this to a non-zero value will enable the verifier // log, populating the [ebpf.Program.VerifierLog] field on successful loads // and including detailed verifier errors if the program is rejected. This // will always allocate an output buffer, but will result in only a single // attempt at loading the program. LogLevel LogLevel // Starting size of the verifier log buffer. If the verifier log is larger // than this size, the buffer will be grown to fit the entire log. Leave at // its default value unless troubleshooting. LogSizeStart uint32 // Disables the verifier log completely, regardless of other options. LogDisabled bool // Type information used for CO-RE relocations. // // This is useful in environments where the kernel BTF is not available // (containers) or where it is in a non-standard location. Defaults to // use the kernel BTF from a well-known location if nil. KernelTypes *btf.Spec // Additional targets to consider for CO-RE relocations. This can be used to // pass BTF information for kernel modules when it's not present on // KernelTypes. ExtraRelocationTargets []*btf.Spec } // ProgramSpec defines a Program. type ProgramSpec struct { // Name is passed to the kernel as a debug aid. // // Unsupported characters will be stripped. Name string // Type determines at which hook in the kernel a program will run. Type ProgramType // Network interface index the user intends to attach this program to after // loading. Only valid for some program types. // // Provides driver-specific context about the target interface to the // verifier, required when using certain BPF helpers. Ifindex uint32 // AttachType of the program, needed to differentiate allowed context // accesses in some newer program types like CGroupSockAddr. // // Available on kernels 4.17 and later. AttachType AttachType // Name of a kernel data structure or function to attach to. Its // interpretation depends on Type and AttachType. AttachTo string // The program to attach to. Must be provided manually. AttachTarget *Program // The name of the ELF section this program originated from. SectionName string Instructions asm.Instructions // Flags is passed to the kernel and specifies additional program // load attributes. Flags uint32 // License of the program. Some helpers are only available if // the license is deemed compatible with the GPL. // // See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1 License string // Version used by Kprobe programs. // // Deprecated on kernels 5.0 and later. Leave empty to let the library // detect this value automatically. KernelVersion uint32 // The byte order this program was compiled for, may be nil. ByteOrder binary.ByteOrder } // Copy returns a copy of the spec. func (ps *ProgramSpec) Copy() *ProgramSpec { if ps == nil { return nil } cpy := *ps cpy.Instructions = make(asm.Instructions, len(ps.Instructions)) copy(cpy.Instructions, ps.Instructions) return &cpy } // Tag calculates the kernel tag for a series of instructions. // // Use asm.Instructions.Tag if you need to calculate for non-native endianness. // // Deprecated: The value produced by this method no longer matches tags produced // by the kernel since Linux 6.18. Use [ProgramSpec.Compatible] instead. func (ps *ProgramSpec) Tag() (string, error) { return ps.Instructions.Tag(internal.NativeEndian) } // Compatible returns nil if a loaded Program's kernel tag matches the one of // the ProgramSpec. // // Returns [ErrProgIncompatible] if the tags do not match. func (ps *ProgramSpec) Compatible(info *ProgramInfo) error { if platform.IsWindows { return fmt.Errorf("%w: Windows does not support tag readback from kernel", internal.ErrNotSupportedOnOS) } ok, err := ps.Instructions.HasTag(info.Tag, internal.NativeEndian) if err != nil { return err } if !ok { return fmt.Errorf("%w: ProgramSpec and Program tags do not match", ErrProgIncompatible) } return nil } // targetsKernelModule returns true if the program supports being attached to a // symbol provided by a kernel module. func (ps *ProgramSpec) targetsKernelModule() bool { if ps.AttachTo == "" { return false } switch ps.Type { case Tracing: switch ps.AttachType { case AttachTraceFEntry, AttachTraceFExit: return true } case Kprobe: return true } return false } // VerifierError is returned by [NewProgram] and [NewProgramWithOptions] if a // program is rejected by the verifier. // // Use [errors.As] to access the error. type VerifierError = internal.VerifierError // Program represents BPF program loaded into the kernel. // // It is not safe to close a Program which is used by other goroutines. type Program struct { // Contains the output of the kernel verifier if enabled, // otherwise it is empty. VerifierLog string fd *sys.FD name string pinnedPath string typ ProgramType } // NewProgram creates a new Program. // // See [NewProgramWithOptions] for details. // // Returns a [VerifierError] containing the full verifier log if the program is // rejected by the kernel. func NewProgram(spec *ProgramSpec) (*Program, error) { return NewProgramWithOptions(spec, ProgramOptions{}) } // NewProgramWithOptions creates a new Program. // // Loading a program for the first time will perform // feature detection by loading small, temporary programs. // // Returns a [VerifierError] containing the full verifier log if the program is // rejected by the kernel. func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { if spec == nil { return nil, errors.New("can't load a program from a nil spec") } prog, err := newProgramWithOptions(spec, opts, btf.NewCache()) if errors.Is(err, asm.ErrUnsatisfiedMapReference) { return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err) } return prog, err } var ( coreBadLoad = []byte(fmt.Sprintf("(18) r10 = 0x%x\n", btf.COREBadRelocationSentinel)) // This log message was introduced by ebb676daa1a3 ("bpf: Print function name in // addition to function id") which first appeared in v4.10 and has remained // unchanged since. coreBadCall = []byte(fmt.Sprintf("invalid func unknown#%d\n", btf.COREBadRelocationSentinel)) kfuncBadCall = []byte(fmt.Sprintf("invalid func unknown#%d\n", kfuncCallPoisonBase)) ) func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) (*Program, error) { if len(spec.Instructions) == 0 { return nil, errors.New("instructions cannot be empty") } if spec.Type == UnspecifiedProgram { return nil, errors.New("can't load program of unspecified type") } if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian { return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian) } // Kernels before 5.0 (6c4fc209fcf9 "bpf: remove useless version check for prog load") // require the version field to be set to the value of the KERNEL_VERSION // macro for kprobe-type programs. // Overwrite Kprobe program version if set to zero or the magic version constant. kv := spec.KernelVersion if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) { v, err := linux.KernelVersion() if err != nil { return nil, fmt.Errorf("detecting kernel version: %w", err) } kv = v.Kernel() } p, progType := platform.DecodeConstant(spec.Type) if p != platform.Native { return nil, fmt.Errorf("program type %s (%s): %w", spec.Type, p, internal.ErrNotSupportedOnOS) } attr := &sys.ProgLoadAttr{ ProgName: maybeFillObjName(spec.Name), ProgType: sys.ProgType(progType), ProgFlags: spec.Flags, ProgIfindex: spec.Ifindex, ExpectedAttachType: sys.AttachType(spec.AttachType), License: sys.NewStringPointer(spec.License), KernVersion: kv, } insns := make(asm.Instructions, len(spec.Instructions)) copy(insns, spec.Instructions) var b btf.Builder if err := applyRelocations(insns, spec.ByteOrder, &b, c, opts.KernelTypes, opts.ExtraRelocationTargets); err != nil { return nil, fmt.Errorf("apply CO-RE relocations: %w", err) } errExtInfos := haveProgramExtInfos() if !b.Empty() && errors.Is(errExtInfos, ErrNotSupported) { // There is at least one CO-RE relocation which relies on a stable local // type ID. // Return ErrNotSupported instead of E2BIG if there is no BTF support. return nil, errExtInfos } if errExtInfos == nil { // Only add func and line info if the kernel supports it. This allows // BPF compiled with modern toolchains to work on old kernels. fib, lib, err := btf.MarshalExtInfos(insns, &b) if err != nil { return nil, fmt.Errorf("marshal ext_infos: %w", err) } attr.FuncInfoRecSize = btf.FuncInfoSize attr.FuncInfoCnt = uint32(len(fib)) / btf.FuncInfoSize attr.FuncInfo = sys.SlicePointer(fib) attr.LineInfoRecSize = btf.LineInfoSize attr.LineInfoCnt = uint32(len(lib)) / btf.LineInfoSize attr.LineInfo = sys.SlicePointer(lib) } if !b.Empty() { handle, err := btf.NewHandle(&b) if err != nil { return nil, fmt.Errorf("load BTF: %w", err) } defer handle.Close() attr.ProgBtfFd = uint32(handle.FD()) } kconfig, err := resolveKconfigReferences(insns) if err != nil { return nil, fmt.Errorf("resolve .kconfig: %w", err) } defer kconfig.Close() if err := resolveKsymReferences(insns); err != nil { return nil, fmt.Errorf("resolve .ksyms: %w", err) } if err := fixupAndValidate(insns); err != nil { return nil, err } handles, err := fixupKfuncs(insns, c) if err != nil { return nil, fmt.Errorf("fixing up kfuncs: %w", err) } defer handles.Close() if len(handles) > 0 { fdArray := handles.fdArray() attr.FdArray = sys.SlicePointer(fdArray) } buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) err = insns.Marshal(buf, internal.NativeEndian) if err != nil { return nil, err } bytecode := buf.Bytes() attr.Insns = sys.SlicePointer(bytecode) attr.InsnCnt = uint32(len(bytecode) / asm.InstructionSize) if spec.AttachTarget != nil { targetID, err := findTargetInProgram(spec.AttachTarget, spec.AttachTo, spec.Type, spec.AttachType) if err != nil { return nil, fmt.Errorf("attach %s/%s: %w", spec.Type, spec.AttachType, err) } attr.AttachBtfId = targetID attr.AttachBtfObjFd = uint32(spec.AttachTarget.FD()) defer runtime.KeepAlive(spec.AttachTarget) } else if spec.AttachTo != "" { var targetMember string attachTo := spec.AttachTo if spec.Type == StructOps { attachTo, targetMember, _ = strings.Cut(attachTo, ":") if targetMember == "" { return nil, fmt.Errorf("struct_ops: AttachTo must be ':' (got %s)", spec.AttachTo) } } module, targetID, err := findProgramTargetInKernel(attachTo, spec.Type, spec.AttachType, c) if err != nil && !errors.Is(err, errUnrecognizedAttachType) { // We ignore errUnrecognizedAttachType since AttachTo may be non-empty // for programs that don't attach anywhere. return nil, fmt.Errorf("attach %s/%s: %w", spec.Type, spec.AttachType, err) } if spec.Type == StructOps { var s *btf.Spec target := btf.Type((*btf.Struct)(nil)) s, module, err = findTargetInKernel(attachTo, &target, c) if err != nil { return nil, fmt.Errorf("lookup struct_ops kern type %q: %w", attachTo, err) } kType := target.(*btf.Struct) targetID, err = s.TypeID(kType) if err != nil { return nil, fmt.Errorf("type id for %s: %w", kType.TypeName(), err) } idx := slices.IndexFunc(kType.Members, func(m btf.Member) bool { return m.Name == targetMember }) if idx < 0 { return nil, fmt.Errorf("member %q not found in %s", targetMember, kType.Name) } // ExpectedAttachType: index of the target member in the struct attr.ExpectedAttachType = sys.AttachType(idx) } attr.AttachBtfId = targetID if module != nil && attr.AttachBtfObjFd == 0 { attr.AttachBtfObjFd = uint32(module.FD()) defer module.Close() } } if platform.IsWindows && opts.LogLevel != 0 { return nil, fmt.Errorf("log level: %w", internal.ErrNotSupportedOnOS) } var logBuf []byte var fd *sys.FD if opts.LogDisabled { // Loading with logging disabled should never retry. fd, err = sys.ProgLoad(attr) if err == nil { return &Program{"", fd, spec.Name, "", spec.Type}, nil } } else { // Only specify log size if log level is also specified. Setting size // without level results in EINVAL. Level will be bumped to LogLevelBranch // if the first load fails. if opts.LogLevel != 0 { attr.LogLevel = opts.LogLevel attr.LogSize = internal.Between(opts.LogSizeStart, minVerifierLogSize, maxVerifierLogSize) } attempts := 1 for { if attr.LogLevel != 0 { logBuf = make([]byte, attr.LogSize) attr.LogBuf = sys.SlicePointer(logBuf) } fd, err = sys.ProgLoad(attr) if err == nil { return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type}, nil } if !retryLogAttrs(attr, opts.LogSizeStart, err) { break } if attempts >= maxVerifierAttempts { return nil, fmt.Errorf("load program: %w (bug: hit %d verifier attempts)", err, maxVerifierAttempts) } attempts++ } } end := bytes.IndexByte(logBuf, 0) if end < 0 { end = len(logBuf) } tail := logBuf[max(end-256, 0):end] switch { case errors.Is(err, unix.EPERM): if len(logBuf) > 0 && logBuf[0] == 0 { // EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can // check that the log is empty to reduce false positives. return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err) } case errors.Is(err, unix.EFAULT): // EFAULT is returned when the kernel hits a verifier bug, and always // overrides ENOSPC, defeating the buffer growth strategy. Warn the user // that they may need to increase the buffer size manually. return nil, fmt.Errorf("load program: %w (hit verifier bug, increase LogSizeStart to fit the log and check dmesg)", err) case errors.Is(err, unix.EINVAL): if bytes.Contains(tail, coreBadCall) { err = errBadRelocation break } else if bytes.Contains(tail, kfuncBadCall) { err = errUnknownKfunc break } case errors.Is(err, unix.EACCES): if bytes.Contains(tail, coreBadLoad) { err = errBadRelocation break } } // hasFunctionReferences may be expensive, so check it last. if (errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM)) && hasFunctionReferences(spec.Instructions) { if err := haveBPFToBPFCalls(); err != nil { return nil, fmt.Errorf("load program: %w", err) } } return nil, internal.ErrorWithLog("load program", err, logBuf) } func retryLogAttrs(attr *sys.ProgLoadAttr, startSize uint32, err error) bool { if attr.LogSize == maxVerifierLogSize { // Maximum buffer size reached, don't grow or retry. return false } // ENOSPC means the log was enabled on the previous iteration, so we only // need to grow the buffer. if errors.Is(err, unix.ENOSPC) { if attr.LogTrueSize != 0 { // Kernel supports LogTrueSize and previous iteration undershot the buffer // size. Try again with the given true size. attr.LogSize = attr.LogTrueSize return true } // Ensure the size doesn't overflow. const factor = 2 if attr.LogSize >= maxVerifierLogSize/factor { attr.LogSize = maxVerifierLogSize return true } // Make an educated guess how large the buffer should be by multiplying. Due // to int division, this rounds down odd sizes. attr.LogSize = internal.Between(attr.LogSize, minVerifierLogSize, maxVerifierLogSize/factor) attr.LogSize *= factor return true } if attr.LogLevel == 0 { // Loading the program failed, it wasn't a buffer-related error, and the log // was disabled the previous iteration. Enable basic logging and retry. attr.LogLevel = LogLevelBranch attr.LogSize = internal.Between(startSize, minVerifierLogSize, maxVerifierLogSize) return true } // Loading the program failed for a reason other than buffer size and the log // was already enabled the previous iteration. Don't retry. return false } // NewProgramFromFD creates a [Program] around a raw fd. // // You should not use fd after calling this function. // // Requires at least Linux 4.13. Returns an error on Windows. func NewProgramFromFD(fd int) (*Program, error) { f, err := sys.NewFD(fd) if err != nil { return nil, err } return newProgramFromFD(f) } // NewProgramFromID returns the [Program] for a given program id. Returns // [ErrNotExist] if there is no eBPF program with the given id. // // Requires at least Linux 4.13. func NewProgramFromID(id ProgramID) (*Program, error) { fd, err := sys.ProgGetFdById(&sys.ProgGetFdByIdAttr{ Id: uint32(id), }) if err != nil { return nil, fmt.Errorf("get program by id: %w", err) } return newProgramFromFD(fd) } func newProgramFromFD(fd *sys.FD) (*Program, error) { info, err := minimalProgramInfoFromFd(fd) if err != nil { fd.Close() return nil, fmt.Errorf("discover program type: %w", err) } return &Program{"", fd, info.Name, "", info.Type}, nil } func (p *Program) String() string { if p.name != "" { return fmt.Sprintf("%s(%s)#%v", p.typ, p.name, p.fd) } return fmt.Sprintf("%s(%v)", p.typ, p.fd) } // Type returns the underlying type of the program. func (p *Program) Type() ProgramType { return p.typ } // Info returns metadata about the program. // // Requires at least 4.10. func (p *Program) Info() (*ProgramInfo, error) { return newProgramInfoFromFd(p.fd) } // Stats returns runtime statistics about the Program. Requires BPF statistics // collection to be enabled, see [EnableStats]. // // Requires at least Linux 5.8. func (p *Program) Stats() (*ProgramStats, error) { return newProgramStatsFromFd(p.fd) } // Handle returns a reference to the program's type information in the kernel. // // Returns ErrNotSupported if the kernel has no BTF support, or if there is no // BTF associated with the program. func (p *Program) Handle() (*btf.Handle, error) { info, err := p.Info() if err != nil { return nil, err } id, ok := info.BTFID() if !ok { return nil, fmt.Errorf("program %s: retrieve BTF ID: %w", p, ErrNotSupported) } return btf.NewHandleFromID(id) } // FD gets the file descriptor of the Program. // // It is invalid to call this function after Close has been called. func (p *Program) FD() int { return p.fd.Int() } // Clone creates a duplicate of the Program. // // Closing the duplicate does not affect the original, and vice versa. // // Cloning a nil Program returns nil. func (p *Program) Clone() (*Program, error) { if p == nil { return nil, nil } dup, err := p.fd.Dup() if err != nil { return nil, fmt.Errorf("can't clone program: %w", err) } return &Program{p.VerifierLog, dup, p.name, "", p.typ}, nil } // Pin persists the Program on the BPF virtual file system past the lifetime of // the process that created it // // Calling Pin on a previously pinned program will overwrite the path, except when // the new path already exists. Re-pinning across filesystems is not supported. // // This requires bpffs to be mounted above fileName. // See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd func (p *Program) Pin(fileName string) error { if err := sys.Pin(p.pinnedPath, fileName, p.fd); err != nil { return err } p.pinnedPath = fileName return nil } // Unpin removes the persisted state for the Program from the BPF virtual filesystem. // // Failed calls to Unpin will not alter the state returned by IsPinned. // // Unpinning an unpinned Program returns nil. func (p *Program) Unpin() error { if err := sys.Unpin(p.pinnedPath); err != nil { return err } p.pinnedPath = "" return nil } // IsPinned returns true if the Program has a non-empty pinned path. func (p *Program) IsPinned() bool { return p.pinnedPath != "" } // Close the Program's underlying file descriptor, which could unload // the program from the kernel if it is not pinned or attached to a // kernel hook. func (p *Program) Close() error { if p == nil { return nil } return p.fd.Close() } // Various options for Run'ing a Program type RunOptions struct { // Program's data input. Required field. // // The kernel expects at least 14 bytes input for an ethernet header for // XDP and SKB programs. Data []byte // Program's data after Program has run. Caller must allocate. Optional field. DataOut []byte // Program's context input. Optional field. Context interface{} // Program's context after Program has run. Must be a pointer or slice. Optional field. ContextOut interface{} // Minimum number of times to run Program. Optional field. Defaults to 1. // // The program may be executed more often than this due to interruptions, e.g. // when runtime.AllThreadsSyscall is invoked. Repeat uint32 // Optional flags. Flags uint32 // CPU to run Program on. Optional field. // Note not all program types support this field. CPU uint32 // BatchSize (default 64) affects the kernel's packet buffer allocation behaviour when running // programs with BPF_F_TEST_XDP_LIVE_FRAMES and a non-zero [RunOptions.Repeat] value. // For more details, see the kernel documentation on BPF_PROG_RUN: // https://docs.kernel.org/bpf/bpf_prog_run.html#running-xdp-programs-in-live-frame-mode BatchSize uint32 // Called whenever the syscall is interrupted, and should be set to testing.B.ResetTimer // or similar. Typically used during benchmarking. Optional field. // // Deprecated: use [testing.B.ReportMetric] with unit "ns/op" instead. Reset func() } // Test runs the Program in the kernel with the given input and returns the // value returned by the eBPF program. // // Note: the kernel expects at least 14 bytes input for an ethernet header for // XDP and SKB programs. // // This function requires at least Linux 4.12. func (p *Program) Test(in []byte) (uint32, []byte, error) { // Older kernels ignore the dataSizeOut argument when copying to user space. // Combined with things like bpf_xdp_adjust_head() we don't really know what the final // size will be. Hence we allocate an output buffer which we hope will always be large // enough, and panic if the kernel wrote past the end of the allocation. // See https://patchwork.ozlabs.org/cover/1006822/ var out []byte if len(in) > 0 { out = make([]byte, len(in)+outputPad) } opts := RunOptions{ Data: in, DataOut: out, Repeat: 1, } ret, _, err := p.run(&opts) if err != nil { return ret, nil, fmt.Errorf("test program: %w", err) } return ret, opts.DataOut, nil } // Run runs the Program in kernel with given RunOptions. // // Note: the same restrictions from Test apply. func (p *Program) Run(opts *RunOptions) (uint32, error) { if opts == nil { opts = &RunOptions{} } ret, _, err := p.run(opts) if err != nil { return ret, fmt.Errorf("run program: %w", err) } return ret, nil } // Benchmark runs the Program with the given input for a number of times // and returns the time taken per iteration. // // Returns the result of the last execution of the program and the time per // run or an error. reset is called whenever the benchmark syscall is // interrupted, and should be set to testing.B.ResetTimer or similar. // // This function requires at least Linux 4.12. func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) { if uint(repeat) > math.MaxUint32 { return 0, 0, fmt.Errorf("repeat is too high") } opts := RunOptions{ Data: in, Repeat: uint32(repeat), Reset: reset, } ret, total, err := p.run(&opts) if err != nil { return ret, total, fmt.Errorf("benchmark program: %w", err) } return ret, total, nil } var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", func() error { if platform.IsWindows { return nil } prog, err := NewProgram(&ProgramSpec{ // SocketFilter does not require privileges on newer kernels. Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", }) if err != nil { // This may be because we lack sufficient permissions, etc. return err } defer prog.Close() in := internal.EmptyBPFContext attr := sys.ProgRunAttr{ ProgFd: uint32(prog.FD()), DataSizeIn: uint32(len(in)), DataIn: sys.SlicePointer(in), } err = sys.ProgRun(&attr) switch { case errors.Is(err, unix.EINVAL): // Check for EINVAL specifically, rather than err != nil since we // otherwise misdetect due to insufficient permissions. return internal.ErrNotSupported case errors.Is(err, unix.EINTR): // We know that PROG_TEST_RUN is supported if we get EINTR. return nil case errors.Is(err, sys.ENOTSUPP): // The first PROG_TEST_RUN patches shipped in 4.12 didn't include // a test runner for SocketFilter. ENOTSUPP means PROG_TEST_RUN is // supported, but not for the program type used in the probe. return nil } return err }, "4.12", "windows:0.20") func (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) { if uint(len(opts.Data)) > math.MaxUint32 { return 0, 0, fmt.Errorf("input is too long") } if err := haveProgRun(); err != nil { return 0, 0, err } var ctxIn []byte if opts.Context != nil { var err error ctxIn, err = binary.Append(nil, internal.NativeEndian, opts.Context) if err != nil { return 0, 0, fmt.Errorf("cannot serialize context: %v", err) } } var ctxOut []byte if opts.ContextOut != nil { ctxOut = make([]byte, binary.Size(opts.ContextOut)) } else if platform.IsWindows && len(ctxIn) > 0 { // Windows rejects a non-zero ctxIn with a nil ctxOut. ctxOut = make([]byte, len(ctxIn)) } attr := sys.ProgRunAttr{ ProgFd: p.fd.Uint(), DataSizeIn: uint32(len(opts.Data)), DataSizeOut: uint32(len(opts.DataOut)), DataIn: sys.SlicePointer(opts.Data), DataOut: sys.SlicePointer(opts.DataOut), Repeat: uint32(opts.Repeat), CtxSizeIn: uint32(len(ctxIn)), CtxSizeOut: uint32(len(ctxOut)), CtxIn: sys.SlicePointer(ctxIn), CtxOut: sys.SlicePointer(ctxOut), Flags: opts.Flags, Cpu: opts.CPU, BatchSize: opts.BatchSize, } if p.Type() == Syscall && ctxIn != nil && ctxOut != nil { // Linux syscall program errors on non-nil ctxOut, uses ctxIn // for both input and output. Shield the user from this wart. if len(ctxIn) != len(ctxOut) { return 0, 0, errors.New("length mismatch: Context and ContextOut") } attr.CtxOut, attr.CtxSizeOut = sys.TypedPointer[uint8]{}, 0 ctxOut = ctxIn } retry: for { err := sys.ProgRun(&attr) if err == nil { break retry } if errors.Is(err, unix.EINTR) { if attr.Repeat <= 1 { // Older kernels check whether enough repetitions have been // executed only after checking for pending signals. // // run signal? done? run ... // // As a result we can get EINTR for repeat==1 even though // the program was run exactly once. Treat this as a // successful run instead. // // Since commit 607b9cc92bd7 ("bpf: Consolidate shared test timing code") // the conditions are reversed: // run done? signal? ... break retry } if opts.Reset != nil { opts.Reset() } continue retry } if errors.Is(err, sys.ENOTSUPP) { return 0, 0, fmt.Errorf("kernel doesn't support running %s: %w", p.Type(), ErrNotSupported) } return 0, 0, err } if opts.DataOut != nil { if int(attr.DataSizeOut) > cap(opts.DataOut) { // Houston, we have a problem. The program created more data than we allocated, // and the kernel wrote past the end of our buffer. panic("kernel wrote past end of output buffer") } opts.DataOut = opts.DataOut[:int(attr.DataSizeOut)] } if opts.ContextOut != nil { b := bytes.NewReader(ctxOut) if err := binary.Read(b, internal.NativeEndian, opts.ContextOut); err != nil { return 0, 0, fmt.Errorf("failed to decode ContextOut: %v", err) } } total := time.Duration(attr.Duration) * time.Nanosecond return attr.Retval, total, nil } func unmarshalProgram(buf sysenc.Buffer) (*Program, error) { var id uint32 if err := buf.Unmarshal(&id); err != nil { return nil, err } // Looking up an entry in a nested map or prog array returns an id, // not an fd. return NewProgramFromID(ProgramID(id)) } func marshalProgram(p *Program, length int) ([]byte, error) { if p == nil { return nil, errors.New("can't marshal a nil Program") } if length != 4 { return nil, fmt.Errorf("can't marshal program to %d bytes", length) } buf := make([]byte, 4) internal.NativeEndian.PutUint32(buf, p.fd.Uint()) return buf, nil } // LoadPinnedProgram loads a Program from a pin (file) on the BPF virtual // filesystem. // // Requires at least Linux 4.11. func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) { fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ Pathname: sys.NewStringPointer(fileName), FileFlags: opts.Marshal(), }) if err != nil { return nil, err } if typ != sys.BPF_TYPE_PROG { _ = fd.Close() return nil, fmt.Errorf("%s is not a Program", fileName) } p, err := newProgramFromFD(fd) if err == nil { p.pinnedPath = fileName if haveObjName() != nil { p.name = filepath.Base(fileName) } } return p, err } // ProgramGetNextID returns the ID of the next eBPF program. // // Returns ErrNotExist, if there is no next eBPF program. func ProgramGetNextID(startID ProgramID) (ProgramID, error) { attr := &sys.ProgGetNextIdAttr{Id: uint32(startID)} return ProgramID(attr.NextId), sys.ProgGetNextId(attr) } // BindMap binds map to the program and is only released once program is released. // // This may be used in cases where metadata should be associated with the program // which otherwise does not contain any references to the map. func (p *Program) BindMap(m *Map) error { attr := &sys.ProgBindMapAttr{ ProgFd: uint32(p.FD()), MapFd: uint32(m.FD()), } return sys.ProgBindMap(attr) } var errUnrecognizedAttachType = errors.New("unrecognized attach type") // find an attach target type in the kernel. // // name, progType and attachType determine which type we need to attach to. // // The attach target may be in a loaded kernel module. // In that case the returned handle will be non-nil. // The caller is responsible for closing the handle. // // Returns errUnrecognizedAttachType if the combination of progType and attachType // is not recognised. func findProgramTargetInKernel(name string, progType ProgramType, attachType AttachType, cache *btf.Cache) (*btf.Handle, btf.TypeID, error) { type match struct { p ProgramType a AttachType } var ( typeName, featureName string target btf.Type ) switch (match{progType, attachType}) { case match{StructOps, AttachStructOps}: typeName = name featureName = "struct_ops " + name target = (*btf.Struct)(nil) case match{LSM, AttachLSMMac}: typeName = "bpf_lsm_" + name featureName = name + " LSM hook" target = (*btf.Func)(nil) case match{Tracing, AttachTraceIter}: typeName = "bpf_iter_" + name featureName = name + " iterator" target = (*btf.Func)(nil) case match{Tracing, AttachTraceFEntry}: typeName = name featureName = fmt.Sprintf("fentry %s", name) target = (*btf.Func)(nil) case match{Tracing, AttachTraceFExit}: typeName = name featureName = fmt.Sprintf("fexit %s", name) target = (*btf.Func)(nil) case match{Tracing, AttachModifyReturn}: typeName = name featureName = fmt.Sprintf("fmod_ret %s", name) target = (*btf.Func)(nil) case match{Tracing, AttachTraceRawTp}: typeName = fmt.Sprintf("btf_trace_%s", name) featureName = fmt.Sprintf("raw_tp %s", name) target = (*btf.Typedef)(nil) default: return nil, 0, errUnrecognizedAttachType } spec, module, err := findTargetInKernel(typeName, &target, cache) if errors.Is(err, btf.ErrNotFound) { return nil, 0, &internal.UnsupportedFeatureError{Name: featureName} } // See cilium/ebpf#894. Until we can disambiguate between equally-named kernel // symbols, we should explicitly refuse program loads. They will not reliably // do what the caller intended. if errors.Is(err, btf.ErrMultipleMatches) { return nil, 0, fmt.Errorf("attaching to ambiguous kernel symbol is not supported: %w", err) } if err != nil { return nil, 0, fmt.Errorf("find target for %s: %w", featureName, err) } id, err := spec.TypeID(target) if err != nil { module.Close() return nil, 0, err } return module, id, nil } // findTargetInKernel attempts to find a named type in the current kernel. // // target will point at the found type after a successful call. Searches both // vmlinux and any loaded modules. // // Returns a non-nil handle if the type was found in a module, or btf.ErrNotFound // if the type wasn't found at all. func findTargetInKernel(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) { kernelSpec, err := cache.Kernel() if err != nil { return nil, nil, err } err = kernelSpec.TypeByName(typeName, target) if errors.Is(err, btf.ErrNotFound) { spec, module, err := findTargetInModule(typeName, target, cache) if err != nil { // EPERM may be returned when we do not have CAP_SYS_ADMIN. // Wrap error with btf.ErrNotFound so callers can handle it accordingly. if errors.Is(err, unix.EPERM) { return spec, nil, fmt.Errorf("find target in modules: %w (%w)", btf.ErrNotFound, err) } return nil, nil, fmt.Errorf("find target in modules: %w", err) } return spec, module, nil } if err != nil { return nil, nil, fmt.Errorf("find target in vmlinux: %w", err) } return kernelSpec, nil, err } // findTargetInModule attempts to find a named type in any loaded module. // // base must contain the kernel's types and is used to parse kmod BTF. Modules // are searched in the order they were loaded. // // Returns btf.ErrNotFound if the target can't be found in any module. func findTargetInModule(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) { it := new(btf.HandleIterator) defer it.Handle.Close() for it.Next() { info, err := it.Handle.Info() if err != nil { return nil, nil, fmt.Errorf("get info for BTF ID %d: %w", it.ID, err) } if !info.IsModule() { continue } spec, err := cache.Module(info.Name) if err != nil { return nil, nil, fmt.Errorf("parse types for module %s: %w", info.Name, err) } err = spec.TypeByName(typeName, target) if errors.Is(err, btf.ErrNotFound) { continue } if err != nil { return nil, nil, fmt.Errorf("lookup type in module %s: %w", info.Name, err) } return spec, it.Take(), nil } if err := it.Err(); err != nil { return nil, nil, fmt.Errorf("iterate modules: %w", err) } return nil, nil, btf.ErrNotFound } // find an attach target type in a program. // // Returns errUnrecognizedAttachType. func findTargetInProgram(prog *Program, name string, progType ProgramType, attachType AttachType) (btf.TypeID, error) { type match struct { p ProgramType a AttachType } var typeName string switch (match{progType, attachType}) { case match{Extension, AttachNone}, match{Tracing, AttachTraceFEntry}, match{Tracing, AttachTraceFExit}: typeName = name default: return 0, errUnrecognizedAttachType } btfHandle, err := prog.Handle() if err != nil { return 0, fmt.Errorf("load target BTF: %w", err) } defer btfHandle.Close() spec, err := btfHandle.Spec(nil) if err != nil { return 0, err } var targetFunc *btf.Func err = spec.TypeByName(typeName, &targetFunc) if err != nil { return 0, fmt.Errorf("find target %s: %w", typeName, err) } return spec.TypeID(targetFunc) } golang-github-cilium-ebpf-0.21.0+ds1/prog_linux_test.go000066400000000000000000000100451520243672000227530ustar00rootroot00000000000000package ebpf import ( "fmt" "math" "runtime" "slices" "testing" "time" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) func TestProgramTestRunInterrupt(t *testing.T) { testutils.SkipOnOldKernel(t, "5.0", "EINTR from BPF_PROG_TEST_RUN") prog := createBasicProgram(t) var ( tgid = unix.Getpid() tidChan = make(chan int, 1) exit = make(chan struct{}) errs = make(chan error, 1) timeout = time.After(5 * time.Second) ) defer close(exit) go func() { runtime.LockOSThread() defer func() { // Wait for the test to allow us to unlock the OS thread, to // ensure that we don't send SIGUSR1 to the wrong thread. <-exit runtime.UnlockOSThread() }() tidChan <- unix.Gettid() // Block this thread in the BPF syscall, so that we can // trigger EINTR by sending a signal. opts := RunOptions{ Data: internal.EmptyBPFContext, Repeat: math.MaxInt32, Reset: func() { // We don't know how long finishing the // test run would take, so flag that we've seen // an interruption and abort the goroutine. close(errs) runtime.Goexit() }, } _, _, err := prog.run(&opts) errs <- err }() tid := <-tidChan for { err := unix.Tgkill(tgid, tid, unix.SIGUSR1) if err != nil { t.Fatal("Can't send signal to goroutine thread:", err) } select { case err, ok := <-errs: if !ok { return } testutils.SkipIfNotSupported(t, err) if err == nil { t.Fatal("testRun wasn't interrupted") } t.Fatal("testRun returned an error:", err) case <-timeout: t.Fatal("Timed out trying to interrupt the goroutine") default: } } } func TestProgramVerifierLogLinux(t *testing.T) { check := func(t *testing.T, err error) { t.Helper() var ve *internal.VerifierError qt.Assert(t, qt.ErrorAs(err, &ve)) loglen := len(fmt.Sprintf("%+v", ve)) qt.Assert(t, qt.IsTrue(loglen > minVerifierLogSize), qt.Commentf("Log buffer didn't grow past minimum, got %d bytes", loglen)) } // Generate a base program of sufficient size whose verifier log does not fit // in the minimum buffer size. Stay under 4096 insn limit of older kernels. var base asm.Instructions for i := 0; i < 4093; i++ { base = append(base, asm.Mov.Reg(asm.R0, asm.R1)) } // Touch R10 (read-only frame pointer) to reliably force a verifier error. invalid := slices.Clone(base) invalid = append(invalid, asm.Mov.Reg(asm.R10, asm.R0)) invalid = append(invalid, asm.Return()) valid := slices.Clone(base) valid = append(valid, asm.Return()) // Start out with testing against the invalid program. spec := &ProgramSpec{ Type: SocketFilter, License: "MIT", Instructions: invalid, } _, err := newProgram(t, spec, nil) check(t, err) // Run tests against a valid program from here on out. spec.Instructions = valid // Explicitly request verifier log for a valid program and a start size. prog := mustNewProgram(t, spec, &ProgramOptions{ LogLevel: LogLevelInstruction, LogSizeStart: minVerifierLogSize * 2, }) qt.Assert(t, qt.IsTrue(len(prog.VerifierLog) > minVerifierLogSize)) } func TestProgramTestRunSyscall(t *testing.T) { testutils.SkipOnOldKernel(t, "5.14", "BPF_PROG_TYPE_SYSCALL") prog := mustNewProgram(t, &ProgramSpec{ Type: Syscall, Flags: sys.BPF_F_SLEEPABLE, License: "MIT", Instructions: []asm.Instruction{ // fn (ctx *u64) { *ctx++; return *ctx } asm.LoadMem(asm.R0, asm.R1, 0, asm.DWord), asm.Add.Imm(asm.R0, 1), asm.StoreMem(asm.R1, 0, asm.R0, asm.DWord), asm.Return(), }, }, nil) // only Context rc, err := prog.Run(&RunOptions{Context: uint64(42)}) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(rc, 43)) // Context and ContextOut out := uint64(0) rc, err = prog.Run(&RunOptions{Context: uint64(99), ContextOut: &out}) if err != nil { t.Fatal(err) } qt.Assert(t, qt.Equals(rc, 100)) qt.Assert(t, qt.Equals(out, 100)) } golang-github-cilium-ebpf-0.21.0+ds1/prog_test.go000066400000000000000000000642701520243672000215450ustar00rootroot00000000000000package ebpf import ( "bytes" "encoding/binary" "errors" "fmt" "math" "os" "path/filepath" "runtime" "strings" "syscall" "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/unix" ) func TestProgramRun(t *testing.T) { pat := []byte{0xDE, 0xAD, 0xBE, 0xEF} buf := internal.EmptyBPFContext // r1 : ctx_start // r1+4: ctx_end ins := asm.Instructions{ // r2 = *(r1+4) asm.LoadMem(asm.R2, asm.R1, 4, asm.Word), // r1 = *(r1+0) asm.LoadMem(asm.R1, asm.R1, 0, asm.Word), // r3 = r1 asm.Mov.Reg(asm.R3, asm.R1), // r3 += len(buf) asm.Add.Imm(asm.R3, int32(len(buf))), // if r3 > r2 goto +len(pat) asm.JGT.Reg(asm.R3, asm.R2, "out"), } for i, b := range pat { ins = append(ins, asm.StoreImm(asm.R1, int16(i), int64(b), asm.Byte)) } ins = append(ins, // return 42 asm.LoadImm(asm.R0, 42, asm.DWord).WithSymbol("out"), asm.Return(), ) if platform.IsWindows { // Windows uses an incompatible context for XDP. Pointers are // 64 bit. // See https://github.com/microsoft/ebpf-for-windows/issues/3873 // r2 = *(r1+8) ins[0] = asm.LoadMem(asm.R2, asm.R1, 8, asm.DWord) // r1 = *(r1+0) ins[1] = asm.LoadMem(asm.R1, asm.R1, 0, asm.DWord) } t.Log(ins) prog := mustNewProgram(t, &ProgramSpec{ Name: "test", Type: XDP, Instructions: ins, License: "MIT", }, nil) p2, err := prog.Clone() if err != nil { t.Fatal("Can't clone program") } defer p2.Close() prog.Close() prog = p2 out := make([]byte, len(buf)) ret := mustRun(t, prog, &RunOptions{Data: buf, DataOut: out}) qt.Assert(t, qt.Equals(ret, 42)) qt.Assert(t, qt.DeepEquals(out[:len(pat)], pat)) } func TestProgramRunWithOptions(t *testing.T) { testutils.SkipOnOldKernel(t, "5.15", "XDP ctx_in/ctx_out") buf := internal.EmptyBPFContext var prog *Program var in, out any if platform.IsWindows { type winSampleProgramContext struct { _ uint64 // data_start (currently leaks kernel pointer) _ uint64 // data_end (currently leaks kernel pointer) Uint32Data uint32 Uint16Data uint16 _ uint16 HelperData1 uint32 HelperData2 uint32 } prog = createProgram(t, WindowsSample, 0) in = &winSampleProgramContext{Uint32Data: 23, HelperData2: 42} out = &winSampleProgramContext{Uint32Data: 23, HelperData2: 42} } else { prog = createProgram(t, XDP, int64(sys.XDP_ABORTED)) in = &sys.XdpMd{Data: 0, DataEnd: uint32(len(buf))} out = &sys.XdpMd{} } opts := RunOptions{ Data: buf, Context: in, ContextOut: out, } ret, err := prog.Run(&opts) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 0 { t.Error("Expected return value to be 0, got", ret) } qt.Assert(t, qt.DeepEquals(out, in)) } func TestProgramRunRawTracepoint(t *testing.T) { testutils.SkipOnOldKernel(t, "5.10", "RawTracepoint test run") prog := createProgram(t, RawTracepoint, 0) ret, err := prog.Run(&RunOptions{}) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if ret != 0 { t.Error("Expected return value to be 0, got", ret) } } func TestProgramRunEmptyData(t *testing.T) { prog := createProgram(t, SocketFilter, 0) _, err := prog.Run(nil) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.ErrorIs(err, unix.EINVAL)) } func TestProgramBenchmark(t *testing.T) { if platform.IsWindows { t.Skip("BPF_PROG_TEST_RUN requires providing context on Windows") } prog := createBasicProgram(t) ret, duration, err := prog.Benchmark(internal.EmptyBPFContext, 1, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Error from Benchmark:", err) } if ret != 2 { t.Error("Expected return value 2, got", ret) } if duration == 0 { t.Error("Expected non-zero duration") } } func TestProgramClose(t *testing.T) { prog := createBasicProgram(t) if err := prog.Close(); err != nil { t.Fatal("Can't close program:", err) } } func TestProgramPin(t *testing.T) { spec := fixupProgramSpec(basicProgramSpec) prog := mustNewProgram(t, spec, nil) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "program") if err := prog.Pin(path); err != nil { t.Fatal(err) } pinned := prog.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) prog.Close() prog, err := LoadPinnedProgram(path, nil) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer prog.Close() qt.Assert(t, qt.Equals(prog.Type(), spec.Type)) if haveObjName() == nil { qt.Assert(t, qt.Equals(prog.name, "test")) } else { qt.Assert(t, qt.Equals(prog.name, "program")) } if !prog.IsPinned() { t.Error("Expected IsPinned to be true") } } func TestProgramUnpin(t *testing.T) { prog := createBasicProgram(t) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "program") if err := prog.Pin(path); err != nil { t.Fatal(err) } pinned := prog.IsPinned() qt.Assert(t, qt.IsTrue(pinned)) if err := prog.Unpin(); err != nil { t.Fatal("Failed to unpin program:", err) } if _, err := os.Stat(path); err == nil { t.Fatal("Pinned program path still exists after unpinning:", err) } } func TestProgramLoadPinnedWithFlags(t *testing.T) { // Introduced in commit 6e71b04a8224. testutils.SkipOnOldKernel(t, "4.14", "file_flags in BPF_OBJ_GET") prog := createBasicProgram(t) tmp := testutils.TempBPFFS(t) path := filepath.Join(tmp, "program") if err := prog.Pin(path); err != nil { t.Fatal(err) } prog.Close() _, err := LoadPinnedProgram(path, &LoadPinOptions{ Flags: math.MaxUint32, }) testutils.SkipIfNotSupported(t, err) if !errors.Is(err, unix.EINVAL) { t.Fatal("Invalid flags don't trigger an error:", err) } } func TestProgramVerifierOutputOnError(t *testing.T) { _, err := newProgram(t, &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.Return(), }, License: "MIT", }, nil) if err == nil { t.Fatal("Expected program to be invalid") } ve, ok := err.(*VerifierError) if !ok { t.Fatal("NewProgram does return an unwrapped VerifierError") } switch { case platform.IsLinux: if !strings.Contains(ve.Error(), "R0 !read_ok") { t.Logf("%+v", ve) t.Error("Missing verifier log in error summary") } case platform.IsWindows: if !strings.Contains(ve.Error(), "r0.type == number") { t.Logf("%+v", ve) t.Error("Missing verifier log in error summary") } default: t.Error("Unsupported platform", runtime.GOOS) } } func TestProgramKernelVersion(t *testing.T) { testutils.SkipOnOldKernel(t, "4.20", "KernelVersion") _ = mustNewProgram(t, &ProgramSpec{ Type: Kprobe, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, KernelVersion: 42, License: "MIT", }, nil) } func TestProgramVerifierLog(t *testing.T) { check := func(t *testing.T, err error) { t.Helper() var ve *internal.VerifierError qt.Assert(t, qt.ErrorAs(err, &ve)) loglen := 0 for _, line := range ve.Log { loglen += len(line) } qt.Assert(t, qt.IsTrue(loglen > 0)) } // Touch R10 (read-only frame pointer) to reliably force a verifier error. invalid := asm.Instructions{ asm.Mov.Reg(asm.R10, asm.R0), asm.Return(), } valid := asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), } // Start out with testing against the invalid program. spec := &ProgramSpec{ Type: SocketFilter, License: "MIT", Instructions: invalid, } // Don't explicitly request a verifier log for an invalid program. _, err := newProgram(t, spec, nil) check(t, err) // Disabling the verifier log should result in a VerifierError without a log. _, err = newProgram(t, spec, &ProgramOptions{ LogDisabled: true, }) var ve *internal.VerifierError qt.Assert(t, qt.ErrorAs(err, &ve)) qt.Assert(t, qt.HasLen(ve.Log, 0)) // Explicitly request a verifier log for an invalid program. _, err = newProgram(t, spec, &ProgramOptions{ LogLevel: LogLevelInstruction, }) check(t, err) // Run tests against a valid program from here on out. spec.Instructions = valid // Don't request a verifier log, expect the valid program to be created // without errors. prog := mustNewProgram(t, spec, nil) qt.Assert(t, qt.HasLen(prog.VerifierLog, 0)) // Explicitly request verifier log for a valid program. prog = mustNewProgram(t, spec, &ProgramOptions{ LogLevel: LogLevelInstruction, }) qt.Assert(t, qt.Not(qt.HasLen(prog.VerifierLog, 0))) } func TestProgramVerifierLogRetry(t *testing.T) { someError := errors.New("not a buffer error") t.Run("retry with oversized buffer, no log_true_size", func(t *testing.T) { // First load failure, without logging enabled. Retry with logging enabled. attr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0} qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError))) qt.Assert(t, qt.Equals(attr.LogLevel, LogLevelBranch)) qt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize)) // Second failure with logging enabled. No buffer error, don't retry. qt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, someError))) qt.Assert(t, qt.Equals(attr.LogLevel, LogLevelBranch)) qt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize)) }) t.Run("retry with oversized buffer, with log_true_size", func(t *testing.T) { // First load failure, without logging enabled. Retry with larger buffer. attr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0} qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError))) // Buffer was sufficiently large and log_true_size was set. Don't retry and // don't modify LogSize to LogTrueSize. attr.LogTrueSize = 123 qt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, someError))) qt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize)) }) t.Run("retry with undersized buffer, no log_true_size", func(t *testing.T) { // First load failure, without logging enabled. Retry with larger buffer. attr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0} qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError))) // Second failure, this time the kernel signals an undersized buffer. Retry // with double the size. qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, unix.ENOSPC))) qt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize*2)) }) t.Run("retry with undersized buffer, with log_true_size", func(t *testing.T) { // First load failure, without logging enabled. Retry with larger buffer. attr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0} qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError))) // Second failure, the kernel signals undersized buffer and also sets // log_true_size. Retry with the exact size required. attr.LogTrueSize = 123 qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, unix.ENOSPC))) qt.Assert(t, qt.Equals(attr.LogSize, 123)) }) t.Run("grow to maximum buffer size", func(t *testing.T) { // Previous loads pushed the log size to (or above) half of the maximum, // which would make it overflow on the next retry. Make sure the log size // actually hits the maximum so we can bail out. attr := &sys.ProgLoadAttr{LogLevel: LogLevelBranch, LogSize: maxVerifierLogSize / 2} qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, unix.ENOSPC))) qt.Assert(t, qt.Equals(attr.LogSize, maxVerifierLogSize)) // Don't retry if the buffer is already at the maximum size, no matter // the return code. qt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, someError))) qt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, unix.ENOSPC))) }) t.Run("start at maximum buffer size", func(t *testing.T) { // The user requested a log buffer exceeding the maximum size, but no log // level. Retry with the maximum size and default log level. attr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0} qt.Assert(t, qt.IsTrue(retryLogAttrs(attr, math.MaxUint32, unix.EINVAL))) qt.Assert(t, qt.Equals(attr.LogLevel, LogLevelBranch)) qt.Assert(t, qt.Equals(attr.LogSize, maxVerifierLogSize)) // Log still doesn't fit maximum-size buffer. Don't retry. qt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, unix.ENOSPC))) }) t.Run("ensure growth terminates within max attempts", func(t *testing.T) { attr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0} var terminated bool for i := 1; i <= maxVerifierAttempts; i++ { if !retryLogAttrs(attr, 0, syscall.ENOSPC) { terminated = true } } qt.Assert(t, qt.IsTrue(terminated)) }) } func TestProgramWithUnsatisfiedMap(t *testing.T) { coll, err := LoadCollectionSpec("testdata/loader-el.elf") if err != nil { t.Fatal(err) } // The program will have at least one map reference. progSpec := coll.Programs["xdp_prog"] progSpec.ByteOrder = nil _, err = newProgram(t, progSpec, nil) if !errors.Is(err, asm.ErrUnsatisfiedMapReference) { t.Fatal("Expected an error wrapping asm.ErrUnsatisfiedMapReference, got", err) } t.Log(err) } func TestProgramName(t *testing.T) { testutils.SkipIfNotSupported(t, haveObjName()) prog := mustNewProgram(t, &ProgramSpec{ Name: "test*123", Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 1, asm.DWord), asm.Return(), }, License: "MIT", }, nil) var info sys.ProgInfo if err := sys.ObjInfo(prog.fd, &info); err != nil { t.Fatal(err) } name := unix.ByteSliceToString(info.Name[:]) qt.Assert(t, qt.Equals(name, "test123")) } func TestProgramCloneNil(t *testing.T) { p, err := (*Program)(nil).Clone() if err != nil { t.Fatal(err) } if p != nil { t.Fatal("Cloning a nil Program doesn't return nil") } } func TestProgramMarshaling(t *testing.T) { const idx = uint32(0) arr := createMap(t, ProgramArray, 1) defer arr.Close() if err := arr.Put(idx, (*Program)(nil)); err == nil { t.Fatal("Put accepted a nil Program") } prog := createBasicProgram(t) if err := arr.Put(idx, prog); err != nil { t.Fatal("Can't put program:", err) } if err := arr.Lookup(idx, Program{}); err == nil { t.Fatal("Lookup accepts non-pointer Program") } var prog2 *Program defer prog2.Close() if err := arr.Lookup(idx, prog2); err == nil { t.Fatal("Get accepts *Program") } testutils.SkipOnOldKernel(t, "4.12", "lookup for ProgramArray") if err := arr.Lookup(idx, &prog2); err != nil { t.Fatal("Can't unmarshal program:", err) } defer prog2.Close() if prog2 == nil { t.Fatal("Unmarshalling set program to nil") } } func TestProgramFromFD(t *testing.T) { spec := fixupProgramSpec(basicProgramSpec) prog := mustNewProgram(t, spec, nil) // If you're thinking about copying this, don't. Use // Clone() instead. prog2, err := NewProgramFromFD(testutils.DupFD(t, prog.FD())) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } defer prog2.Close() // Name and type are supposed to be copied from program info. if haveObjName() == nil && prog2.name != "test" { t.Errorf("Expected program to have name test, got '%s'", prog2.name) } qt.Assert(t, qt.Equals(prog2.Type(), spec.Type)) } func TestHaveProgTestRun(t *testing.T) { testutils.CheckFeatureTest(t, haveProgRun) } func TestProgramGetNextID(t *testing.T) { testutils.SkipOnOldKernel(t, "4.13", "bpf_prog_get_next_id") // Ensure there is at least one program loaded _ = createBasicProgram(t) // As there can be multiple eBPF programs, we loop over all of them and // make sure, the IDs increase and the last call will return ErrNotExist last := ProgramID(0) for { next, err := ProgramGetNextID(last) if errors.Is(err, os.ErrNotExist) { if last == 0 { t.Fatal("Got ErrNotExist on the first iteration") } break } if err != nil { t.Fatal("Unexpected error:", err) } if next <= last { t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last) } last = next } } func TestNewProgramFromID(t *testing.T) { prog := createBasicProgram(t) info, err := prog.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal("Could not get program info:", err) } id, ok := info.ID() if !ok { t.Skip("Program ID not supported") } prog2, err := NewProgramFromID(id) if err != nil { t.Fatalf("Can't get FD for program ID %d: %v", id, err) } prog2.Close() // As there can be multiple programs, we use max(uint32) as ProgramID to trigger an expected error. _, err = NewProgramFromID(ProgramID(math.MaxUint32)) if !errors.Is(err, os.ErrNotExist) { t.Fatal("Expected ErrNotExist, got:", err) } } func TestProgramRejectIncorrectByteOrder(t *testing.T) { spec := basicProgramSpec.Copy() spec.ByteOrder = binary.BigEndian if spec.ByteOrder == internal.NativeEndian { spec.ByteOrder = binary.LittleEndian } _, err := newProgram(t, spec, nil) if err == nil { t.Error("Incorrect ByteOrder should be rejected at load time") } } // This uses unkeyed fields on purpose to force setting a non-zero value when // a new field is added. func TestProgramSpecCopy(t *testing.T) { a := &ProgramSpec{ "test", 1, 1, 1, "attach", nil, // Can't copy Program "section", asm.Instructions{ asm.Return(), }, 1, "license", 1, binary.LittleEndian, } qt.Check(t, qt.IsNil((*ProgramSpec)(nil).Copy())) qt.Assert(t, testutils.IsDeepCopy(a.Copy(), a)) } func TestProgramSpecCompatible(t *testing.T) { arr := createMap(t, Array, 2) spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, -1, asm.DWord), asm.LoadMapPtr(asm.R1, arr.FD()), asm.Mov.Imm32(asm.R0, 0), asm.Return(), }, License: "MIT", } prog := mustNewProgram(t, spec, nil) info, err := prog.Info() testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) err = spec.Compatible(info) testutils.SkipIfNotSupportedOnOS(t, err) qt.Assert(t, qt.IsNil(err)) } func TestProgramAttachToKernel(t *testing.T) { // See https://github.com/torvalds/linux/commit/290248a5b7d829871b3ea3c62578613a580a1744 testutils.SkipOnOldKernel(t, "5.5", "attach_btf_id") tests := []struct { attachTo string programType ProgramType attachType AttachType flags uint32 }{ { attachTo: "task_getpgid", programType: LSM, attachType: AttachLSMMac, }, { attachTo: "inet_dgram_connect", programType: Tracing, attachType: AttachTraceFEntry, }, { attachTo: "inet_dgram_connect", programType: Tracing, attachType: AttachTraceFExit, }, { attachTo: "bpf_modify_return_test", programType: Tracing, attachType: AttachModifyReturn, }, { attachTo: "kfree_skb", programType: Tracing, attachType: AttachTraceRawTp, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachTraceFEntry, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachTraceFExit, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachModifyReturn, }, { attachTo: "bpf_testmod_test_read", programType: Tracing, attachType: AttachTraceRawTp, }, } for _, test := range tests { name := fmt.Sprintf("%s:%s", test.attachType, test.attachTo) t.Run(name, func(t *testing.T) { if strings.HasPrefix(test.attachTo, "bpf_testmod_") { requireTestmod(t) } _ = mustNewProgram(t, &ProgramSpec{ AttachTo: test.attachTo, AttachType: test.attachType, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "GPL", Type: test.programType, Flags: test.flags, }, nil) }) } } func TestProgramKernelTypes(t *testing.T) { if _, err := os.Stat("/sys/kernel/btf/vmlinux"); os.IsNotExist(err) { t.Skip("/sys/kernel/btf/vmlinux not present") } btfSpec, err := btf.LoadSpec("/sys/kernel/btf/vmlinux") if err != nil { t.Fatal(err) } _, err = newProgram(t, &ProgramSpec{ Type: Tracing, AttachType: AttachTraceIter, AttachTo: "bpf_map", Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "MIT", }, &ProgramOptions{ KernelTypes: btfSpec, }) testutils.SkipIfNotSupported(t, err) qt.Assert(t, qt.IsNil(err)) } func TestProgramBindMap(t *testing.T) { testutils.SkipOnOldKernel(t, "5.10", "BPF_PROG_BIND_MAP") arr := createMap(t, Array, 2) prog := createBasicProgram(t) // The attached map does not contain BTF information. So // the metadata part of the program will be empty. This // test just makes sure that we can bind a map to a program. if err := prog.BindMap(arr); err != nil { t.Errorf("Failed to bind map to program: %v", err) } } func TestProgramInstructions(t *testing.T) { name := "test_prog" spec := &ProgramSpec{ Type: SocketFilter, Name: name, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, -1, asm.DWord).WithSymbol(name), asm.Return(), }, License: "MIT", } prog := mustNewProgram(t, spec, nil) pi, err := prog.Info() testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatal(err) } if platform.IsWindows { t.Skip("prog.Info() does not return a valid Tag on Windows") } ok, err := spec.Instructions.HasTag(pi.Tag, internal.NativeEndian) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsTrue(ok), qt.Commentf("ProgramSpec tag differs from xlated instructions")) } func TestProgramLoadErrors(t *testing.T) { testutils.SkipOnOldKernel(t, "4.10", "stable verifier log output") spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/errors-%s.elf")) qt.Assert(t, qt.IsNil(err)) var b btf.Builder raw, err := b.Marshal(nil, nil) qt.Assert(t, qt.IsNil(err)) empty, err := btf.LoadSpecFromReader(bytes.NewReader(raw)) qt.Assert(t, qt.IsNil(err)) for _, test := range []struct { name string want error }{ {"poisoned_single", errBadRelocation}, {"poisoned_double", errBadRelocation}, {"poisoned_kfunc", errUnknownKfunc}, } { progSpec := spec.Programs[test.name] qt.Assert(t, qt.IsNotNil(progSpec)) t.Run(test.name, func(t *testing.T) { t.Log(progSpec.Instructions) _, err := newProgram(t, progSpec, &ProgramOptions{ KernelTypes: empty, }) testutils.SkipIfNotSupported(t, err) var ve *VerifierError qt.Assert(t, qt.ErrorAs(err, &ve)) t.Logf("%-5v", ve) qt.Assert(t, qt.ErrorIs(err, test.want)) }) } } func TestProgramTargetsKernelModule(t *testing.T) { ps := ProgramSpec{Type: Kprobe} qt.Assert(t, qt.IsFalse(ps.targetsKernelModule())) ps.AttachTo = "bpf_testmod_test_read" qt.Assert(t, qt.IsTrue(ps.targetsKernelModule())) } func TestProgramLoadBoundToDevice(t *testing.T) { testutils.SkipOnOldKernel(t, "6.3", "device-bound XDP programs") ins := asm.Instructions{ asm.LoadImm(asm.R0, 2, asm.DWord).WithSymbol("out"), asm.Return(), } _, err := NewProgram(&ProgramSpec{ Type: XDP, Ifindex: math.MaxUint32, AttachType: AttachXDP, Instructions: ins, Flags: sys.BPF_F_XDP_DEV_BOUND_ONLY, License: "MIT", }) testutils.SkipIfNotSupportedOnOS(t, err) // Binding to loopback leads to crashes, yet is only explicitly disallowed // since 3595599fa836 ("net: xdp: Disallow attaching device-bound programs in // generic mode"). This only landed in 6.14 and returns EOPNOTSUPP. // // However, since attaching to loopback quietly succeeds on older kernels, use // a non-existent ifindex to trigger EINVAL on all kernels. Without specifying // ifindex, loading the program succeeds if the kernel knows the // DEV_BOUND_ONLY flag. qt.Assert(t, qt.ErrorIs(err, unix.EINVAL)) } func BenchmarkNewProgram(b *testing.B) { testutils.SkipOnOldKernel(b, "5.18", "kfunc support") spec, err := LoadCollectionSpec(testutils.NativeFile(b, "testdata/kfunc-%s.elf")) qt.Assert(b, qt.IsNil(err)) b.ReportAllocs() for b.Loop() { _, err := NewProgram(spec.Programs["benchmark"]) if !errors.Is(err, unix.EACCES) { b.Fatal("Unexpected error:", err) } } } // Print the full verifier log when loading a program fails. func ExampleVerifierError_retrieveFullLog() { _, err := NewProgram(&ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), // Missing Return }, License: "MIT", }) var ve *VerifierError if errors.As(err, &ve) { // Using %+v will print the whole verifier error, not just the last // few lines. fmt.Printf("Verifier error: %+v\n", ve) } } // VerifierLog understands a variety of formatting flags. func ExampleVerifierError() { err := internal.ErrorWithLog( "catastrophe", syscall.ENOSPC, []byte("first\nsecond\nthird"), ) fmt.Printf("With %%s: %s\n", err) fmt.Printf("All log lines: %+v\n", err) fmt.Printf("First line: %+1v\n", err) fmt.Printf("Last two lines: %-2v\n", err) // Output: With %s: catastrophe: no space left on device: third (2 line(s) omitted) // All log lines: catastrophe: no space left on device: // first // second // third // First line: catastrophe: no space left on device: // first // (2 line(s) omitted) // Last two lines: catastrophe: no space left on device: // (1 line(s) omitted) // second // third } // Use NewProgramWithOptions if you'd like to get the verifier output // for a program, or if you want to change the buffer size used when // generating error messages. func ExampleProgram_retrieveVerifierLog() { spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", } prog, err := NewProgramWithOptions(spec, ProgramOptions{ LogLevel: LogLevelInstruction, }) if err != nil { panic(err) } defer prog.Close() fmt.Println("The verifier output is:") fmt.Println(prog.VerifierLog) } // It's possible to read a program directly from a ProgramArray. func ExampleProgram_unmarshalFromMap() { progArray, err := LoadPinnedMap("/path/to/map", nil) if err != nil { panic(err) } defer progArray.Close() // Load a single program var prog *Program if err := progArray.Lookup(uint32(0), &prog); err != nil { panic(err) } defer prog.Close() fmt.Println("first prog:", prog) // Iterate all programs var ( key uint32 entries = progArray.Iterate() ) for entries.Next(&key, &prog) { fmt.Println(key, "is", prog) } if err := entries.Err(); err != nil { panic(err) } } func ExampleProgramSpec_Compatible() { spec := &ProgramSpec{ Type: SocketFilter, Instructions: asm.Instructions{ asm.LoadImm(asm.R0, 0, asm.DWord), asm.Return(), }, License: "MIT", } prog, _ := NewProgram(spec) info, _ := prog.Info() if err := spec.Compatible(info); err != nil { fmt.Printf("The programs are incompatible: %s\n", err) } else { fmt.Println("The programs are compatible") } } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/000077500000000000000000000000001520243672000206335ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/doc.go000066400000000000000000000004211520243672000217240ustar00rootroot00000000000000// Package ringbuf allows interacting with the BPF ring buffer. // // BPF allows submitting custom events to a BPF ring buffer map set up // by userspace. This is very useful to push things like packet samples // from BPF to a daemon running in user space. package ringbuf golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/helper_other_test.go000066400000000000000000000037201520243672000247030ustar00rootroot00000000000000//go:build !windows package ringbuf import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" ) func mustOutputSamplesProg(tb testing.TB, sampleMessages ...sampleMessage) (*ebpf.Program, *ebpf.Map) { tb.Helper() events, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.RingBuf, MaxEntries: 4096, }) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { events.Close() }) var maxSampleSize int for _, sampleMessage := range sampleMessages { if sampleMessage.size > maxSampleSize { maxSampleSize = sampleMessage.size } } insns := asm.Instructions{ asm.LoadImm(asm.R0, 0x0102030404030201, asm.DWord), asm.Mov.Reg(asm.R9, asm.R1), } bufDwords := (maxSampleSize / 8) + 1 for i := range bufDwords { insns = append(insns, asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord), ) } for _, sampleMessage := range sampleMessages { insns = append(insns, asm.LoadMapPtr(asm.R1, events.FD()), asm.Mov.Imm(asm.R2, int32(sampleMessage.size)), asm.Mov.Imm(asm.R3, int32(0)), asm.FnRingbufReserve.Call(), asm.JEq.Imm(asm.R0, 0, "exit"), asm.Mov.Reg(asm.R5, asm.R0), ) for i := range sampleMessage.size { insns = append(insns, asm.LoadMem(asm.R4, asm.RFP, int16(i+1)*-1, asm.Byte), asm.StoreMem(asm.R5, int16(i), asm.R4, asm.Byte), ) } if sampleMessage.discard { insns = append(insns, asm.Mov.Reg(asm.R1, asm.R5), asm.Mov.Imm(asm.R2, sampleMessage.flags), asm.FnRingbufDiscard.Call(), ) } else { insns = append(insns, asm.Mov.Reg(asm.R1, asm.R5), asm.Mov.Imm(asm.R2, sampleMessage.flags), asm.FnRingbufSubmit.Call(), ) } } insns = append(insns, asm.Mov.Imm(asm.R0, int32(0)).WithSymbol("exit"), asm.Return(), ) prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ License: "MIT", Type: ebpf.XDP, Instructions: insns, }) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { prog.Close() }) return prog, events } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/helper_test.go000066400000000000000000000010531520243672000234770ustar00rootroot00000000000000package ringbuf import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/testutils" ) func mustRun(tb testing.TB, prog *ebpf.Program) { tb.Helper() opts := &ebpf.RunOptions{ Data: internal.EmptyBPFContext, } if platform.IsWindows { opts.Context = make([]byte, 32) } ret, err := prog.Run(opts) testutils.SkipIfNotSupported(tb, err) qt.Assert(tb, qt.IsNil(err)) qt.Assert(tb, qt.Equals(ret, uint32(0))) } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/helper_windows_test.go000066400000000000000000000031361520243672000252550ustar00rootroot00000000000000package ringbuf import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" ) func mustOutputSamplesProg(tb testing.TB, sampleMessages ...sampleMessage) (*ebpf.Program, *ebpf.Map) { tb.Helper() events, err := ebpf.NewMap(&ebpf.MapSpec{ Type: ebpf.WindowsRingBuf, MaxEntries: 4096, }) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { events.Close() }) var maxSampleSize int for _, sampleMessage := range sampleMessages { if sampleMessage.size > maxSampleSize { maxSampleSize = sampleMessage.size } } insns := asm.Instructions{ asm.LoadImm(asm.R0, 0x0102030404030201, asm.DWord), asm.Mov.Reg(asm.R9, asm.R1), } bufDwords := (maxSampleSize / 8) + 1 for i := range bufDwords { insns = append(insns, asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord), ) } for _, sampleMessage := range sampleMessages { if sampleMessage.discard { tb.Skip("discard is not supported on Windows") } insns = append(insns, asm.LoadMapPtr(asm.R1, events.FD()), asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -int32(8*bufDwords)), asm.Mov.Imm(asm.R3, int32(sampleMessage.size)), asm.Mov.Imm(asm.R4, sampleMessage.flags), asm.WindowsFnRingbufOutput.Call(), asm.JNE.Imm(asm.R0, 0, "exit"), ) } insns = append(insns, asm.Mov.Imm(asm.R0, int32(0)), asm.Return().WithSymbol("exit"), ) prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ License: "MIT", Type: ebpf.WindowsSample, Instructions: insns, }) qt.Assert(tb, qt.IsNil(err)) tb.Cleanup(func() { prog.Close() }) return prog, events } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/reader.go000066400000000000000000000124651520243672000224340ustar00rootroot00000000000000package ringbuf import ( "errors" "fmt" "os" "sync" "time" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" ) var ( ErrClosed = os.ErrClosed errEOR = errors.New("end of ring") errBusy = errors.New("sample not committed yet") ) // poller abstracts platform-specific event notification. type poller interface { Wait(deadline time.Time) error Flush() error Close() error } // eventRing abstracts platform-specific ring buffer memory access. type eventRing interface { size() int AvailableBytes() uint64 readRecord(rec *Record) error Close() error } // ringbufHeader from 'struct bpf_ringbuf_hdr' in kernel/bpf/ringbuf.c type ringbufHeader struct { Len uint32 _ uint32 // pg_off, only used by kernel internals } const ringbufHeaderSize = int(unsafe.Sizeof(ringbufHeader{})) func (rh *ringbufHeader) isBusy() bool { return rh.Len&sys.BPF_RINGBUF_BUSY_BIT != 0 } func (rh *ringbufHeader) isDiscard() bool { return rh.Len&sys.BPF_RINGBUF_DISCARD_BIT != 0 } func (rh *ringbufHeader) dataLen() int { return int(rh.Len & ^uint32(sys.BPF_RINGBUF_BUSY_BIT|sys.BPF_RINGBUF_DISCARD_BIT)) } type Record struct { RawSample []byte // The minimum number of bytes remaining in the ring buffer after this Record has been read. Remaining int } // Reader allows reading bpf_ringbuf_output // from user space. type Reader struct { poller poller // mu protects read/write access to the Reader structure mu sync.Mutex ring eventRing haveData bool deadline time.Time bufferSize int pendingErr error } // NewReader creates a new BPF ringbuf reader. func NewReader(ringbufMap *ebpf.Map) (*Reader, error) { if ringbufMap.Type() != ebpf.RingBuf && ringbufMap.Type() != ebpf.WindowsRingBuf { return nil, fmt.Errorf("invalid Map type: %s", ringbufMap.Type()) } maxEntries := int(ringbufMap.MaxEntries()) if maxEntries == 0 || (maxEntries&(maxEntries-1)) != 0 { return nil, fmt.Errorf("ringbuffer map size %d is zero or not a power of two", maxEntries) } poller, err := newPoller(ringbufMap.FD()) if err != nil { return nil, err } ring, err := newRingBufEventRing(ringbufMap.FD(), maxEntries) if err != nil { poller.Close() return nil, fmt.Errorf("failed to create ringbuf ring: %w", err) } return &Reader{ poller: poller, ring: ring, bufferSize: ring.size(), // On Windows, the wait handle is only set when the reader is created, // so we miss any wakeups that happened before. // Do an opportunistic read to get any pending samples. haveData: platform.IsWindows, }, nil } // Close frees resources used by the reader. // // It interrupts calls to Read. func (r *Reader) Close() error { if err := r.poller.Close(); err != nil { if errors.Is(err, os.ErrClosed) { return nil } return err } // Acquire the lock. This ensures that Read isn't running. r.mu.Lock() defer r.mu.Unlock() var err error if r.ring != nil { err = r.ring.Close() r.ring = nil } return err } // SetDeadline controls how long Read and ReadInto will block waiting for samples. // // Passing a zero time.Time will remove the deadline. func (r *Reader) SetDeadline(t time.Time) { r.mu.Lock() defer r.mu.Unlock() r.deadline = t } // Read the next record from the BPF ringbuf. // // Calling [Close] interrupts the method with [os.ErrClosed]. Calling [Flush] // makes it return all records currently in the ring buffer, followed by [ErrFlushed]. // // Returns [os.ErrDeadlineExceeded] if a deadline was set and after all records // have been read from the ring. // // See [ReadInto] for a more efficient version of this method. func (r *Reader) Read() (Record, error) { var rec Record err := r.ReadInto(&rec) return rec, err } // ReadInto is like Read except that it allows reusing Record and associated buffers. func (r *Reader) ReadInto(rec *Record) error { r.mu.Lock() defer r.mu.Unlock() if r.ring == nil { return fmt.Errorf("ringbuffer: %w", ErrClosed) } for { if !r.haveData { if pe := r.pendingErr; pe != nil { r.pendingErr = nil return pe } err := r.poller.Wait(r.deadline) if errors.Is(err, os.ErrDeadlineExceeded) || errors.Is(err, ErrFlushed) { // Ignoring this for reading a valid entry after timeout or flush. // This can occur if the producer submitted to the ring buffer // with BPF_RB_NO_WAKEUP. r.pendingErr = err } else if err != nil { return err } r.haveData = true } for { err := r.ring.readRecord(rec) // Not using errors.Is which is quite a bit slower // For a tight loop it might make a difference if err == errBusy { continue } if err == errEOR { r.haveData = false break } return err } } } // BufferSize returns the size in bytes of the ring buffer func (r *Reader) BufferSize() int { return r.bufferSize } // Flush unblocks Read/ReadInto and successive Read/ReadInto calls will return pending samples at this point, // until you receive a ErrFlushed error. func (r *Reader) Flush() error { return r.poller.Flush() } // AvailableBytes returns the amount of data available to read in the ring buffer in bytes. func (r *Reader) AvailableBytes() int { // Don't need to acquire the lock here since the implementation of AvailableBytes // performs atomic loads on the producer and consumer positions. return int(r.ring.AvailableBytes()) } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/reader_other.go000066400000000000000000000015621520243672000236310ustar00rootroot00000000000000//go:build !windows package ringbuf import ( "time" "github.com/cilium/ebpf/internal/epoll" "github.com/cilium/ebpf/internal/unix" ) var ErrFlushed = epoll.ErrFlushed var _ poller = (*epollPoller)(nil) type epollPoller struct { *epoll.Poller events []unix.EpollEvent } func newPoller(fd int) (*epollPoller, error) { ep, err := epoll.New() if err != nil { return nil, err } if err := ep.Add(fd, 0); err != nil { ep.Close() return nil, err } return &epollPoller{ Poller: ep, events: make([]unix.EpollEvent, 1), }, nil } // Wait blocks until data is available or the deadline is reached. // Returns [os.ErrDeadlineExceeded] if a deadline was set and no wakeup was received. // Returns [ErrFlushed] if the ring buffer was flushed manually. func (p *epollPoller) Wait(deadline time.Time) error { _, err := p.Poller.Wait(p.events, deadline) return err } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/reader_test.go000066400000000000000000000210201520243672000234560ustar00rootroot00000000000000package ringbuf import ( "errors" "os" "testing" "time" "github.com/go-quicktest/qt" "github.com/google/go-cmp/cmp" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" "github.com/cilium/ebpf/internal/testutils/testmain" ) type sampleMessage struct { size int flags int32 discard bool } func TestMain(m *testing.M) { testmain.Run(m) } func TestRingbufReader(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") readerTests := []struct { name string messages []sampleMessage want map[int][]byte }{ { name: "send one short sample", messages: []sampleMessage{{size: 5}}, want: map[int][]byte{ 5: {1, 2, 3, 4, 4}, }, }, { name: "send three short samples, the second is discarded", messages: []sampleMessage{{size: 5}, {size: 10, discard: true}, {size: 15}}, want: map[int][]byte{ 5: {1, 2, 3, 4, 4}, 15: {1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2}, }, }, { name: "send five samples, every even is discarded", messages: []sampleMessage{{size: 5}, {size: 10, discard: true}, {size: 15}, {size: 20, discard: true}, {size: 25}}, want: map[int][]byte{ 5: {1, 2, 3, 4, 4}, 15: {1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2}, 25: {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}, }, }, } for _, tt := range readerTests { t.Run(tt.name, func(t *testing.T) { prog, events := mustOutputSamplesProg(t, tt.messages...) rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() qt.Assert(t, qt.Equals(rd.AvailableBytes(), 0)) if uint32(rd.BufferSize()) != events.MaxEntries() { t.Errorf("expected %d BufferSize, got %d", events.MaxEntries(), rd.BufferSize()) } opts := &ebpf.RunOptions{ Data: internal.EmptyBPFContext, } if platform.IsWindows { opts.Context = make([]byte, 32) } mustRun(t, prog) var avail int for _, m := range tt.messages { avail += ringbufHeaderSize + internal.Align(m.size, 8) } qt.Assert(t, qt.Equals(rd.AvailableBytes(), avail)) raw := make(map[int][]byte) for len(raw) < len(tt.want) { record, err := rd.Read() if err != nil { t.Fatal("Can't read samples:", err) } raw[len(record.RawSample)] = record.RawSample if len(raw) == len(tt.want) { if record.Remaining != 0 { t.Errorf("expected 0 Remaining, got %d", record.Remaining) } } else { if record.Remaining == 0 { t.Error("expected non-zero Remaining, got 0") } } } if diff := cmp.Diff(tt.want, raw); diff != "" { t.Errorf("Read samples mismatch (-want +got):\n%s", diff) } }) } } func TestReaderBlocking(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") prog, events := mustOutputSamplesProg(t, sampleMessage{size: 5, flags: 0}) mustRun(t, prog) rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() if _, err := rd.Read(); err != nil { t.Fatal("Can't read first sample:", err) } errs := make(chan error, 1) go func() { _, err := rd.Read() errs <- err }() select { case err := <-errs: t.Fatal("Read returns error instead of blocking:", err) case <-time.After(100 * time.Millisecond): } // Close should interrupt blocking Read if err := rd.Close(); err != nil { t.Fatal(err) } select { case err := <-errs: if !errors.Is(err, ErrClosed) { t.Fatal("Expected os.ErrClosed from interrupted Read, got:", err) } case <-time.After(time.Second): t.Fatal("Close doesn't interrupt Read") } // And we should be able to call it multiple times if err := rd.Close(); err != nil { t.Fatal(err) } if _, err := rd.Read(); !errors.Is(err, ErrClosed) { t.Fatal("Second Read on a closed RingbufReader doesn't return ErrClosed") } } func TestReaderNoWakeup(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") prog, events := mustOutputSamplesProg(t, sampleMessage{size: 5, flags: sys.BPF_RB_NO_WAKEUP}, // Read after timeout sampleMessage{size: 7, flags: sys.BPF_RB_NO_WAKEUP}, // Read won't block ) rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() qt.Assert(t, qt.Equals(rd.AvailableBytes(), 0)) mustRun(t, prog) qt.Assert(t, qt.Equals(rd.AvailableBytes(), 2*16)) rd.SetDeadline(time.Now()) record, err := rd.Read() if err != nil { t.Error("Expected no error from first Read, got:", err) } if len(record.RawSample) != 5 { t.Errorf("Expected to read 5 bytes but got %d", len(record.RawSample)) } qt.Assert(t, qt.Equals(rd.AvailableBytes(), 1*16)) record, err = rd.Read() if err != nil { t.Error("Expected no error from second Read, got:", err) } if len(record.RawSample) != 7 { t.Errorf("Expected to read 7 bytes but got %d", len(record.RawSample)) } qt.Assert(t, qt.Equals(rd.AvailableBytes(), 0)) _, err = rd.Read() if !errors.Is(err, os.ErrDeadlineExceeded) { t.Errorf("Expected os.ErrDeadlineExceeded from third Read but got %v", err) } } func TestReaderFlushPendingEvents(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") prog, events := mustOutputSamplesProg(t, sampleMessage{size: 5, flags: sys.BPF_RB_NO_WAKEUP}, // Read after Flush sampleMessage{size: 7, flags: sys.BPF_RB_NO_WAKEUP}, // Read won't block ) rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() mustRun(t, prog) wait := make(chan *Record) go func() { wait <- nil record, err := rd.Read() qt.Assert(t, qt.IsNil(err)) wait <- &record }() <-wait time.Sleep(10 * time.Millisecond) err = rd.Flush() qt.Assert(t, qt.IsNil(err)) waitRec := <-wait if waitRec == nil { t.Error("Expected to read record but got nil") } if waitRec != nil && len(waitRec.RawSample) != 5 { t.Errorf("Expected to read 5 bytes but got %d", len(waitRec.RawSample)) } record, err := rd.Read() if err != nil { t.Error("Expected no error from second Read, got:", err) } if len(record.RawSample) != 7 { t.Errorf("Expected to read 7 bytes but got %d", len(record.RawSample)) } _, err = rd.Read() if !errors.Is(err, ErrFlushed) { t.Errorf("Expected ErrFlushed from third Read but got %v", err) } } func TestReaderSetDeadline(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") _, events := mustOutputSamplesProg(t, sampleMessage{size: 5, flags: 0}) rd, err := NewReader(events) if err != nil { t.Fatal(err) } defer rd.Close() rd.SetDeadline(time.Now().Add(-time.Second)) if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from first Read, got:", err) } if _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) { t.Error("Expected os.ErrDeadlineExceeded from second Read, got:", err) } } func TestReadAfterClose(t *testing.T) { testutils.SkipOnOldKernel(t, "5.8", "BPF ring buffer") prog, events := mustOutputSamplesProg(t, sampleMessage{size: 5, flags: 0}, sampleMessage{size: 5, flags: 0}, ) mustRun(t, prog) rd, err := NewReader(events) if err != nil { t.Fatal(err) } _, err = rd.Read() if err != nil { t.Error("Expected no error after first Read, got:", err) } err = rd.Close() if err != nil { t.Error("Expected no error from Close, got: ", err) } _, err = rd.Read() if err == nil || !errors.Is(err, ErrClosed) { t.Error("Expected ErrClosed but got: ", err) } } func BenchmarkReader(b *testing.B) { testutils.SkipOnOldKernel(b, "5.8", "BPF ring buffer") readerBenchmarks := []struct { name string flags int32 }{ { name: "normal epoll with timeout -1", }, } for _, bm := range readerBenchmarks { b.Run(bm.name, func(b *testing.B) { prog, events := mustOutputSamplesProg(b, sampleMessage{size: 80, flags: bm.flags}) rd, err := NewReader(events) if err != nil { b.Fatal(err) } defer rd.Close() b.ReportAllocs() for b.Loop() { b.StopTimer() mustRun(b, prog) b.StartTimer() _, err = rd.Read() if err != nil { b.Fatal("Can't read samples:", err) } } }) } } func BenchmarkReadInto(b *testing.B) { testutils.SkipOnOldKernel(b, "5.8", "BPF ring buffer") prog, events := mustOutputSamplesProg(b, sampleMessage{size: 80, flags: 0}) rd, err := NewReader(events) if err != nil { b.Fatal(err) } defer rd.Close() b.ReportAllocs() var rec Record for b.Loop() { b.StopTimer() mustRun(b, prog) b.StartTimer() if err := rd.ReadInto(&rec); err != nil { b.Fatal("Can't read samples:", err) } } } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/reader_windows.go000066400000000000000000000050041520243672000241750ustar00rootroot00000000000000package ringbuf import ( "errors" "fmt" "os" "sync/atomic" "time" "golang.org/x/sys/windows" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/efw" ) var ErrFlushed = errors.New("ring buffer flushed") var _ poller = (*windowsPoller)(nil) type windowsPoller struct { closed atomic.Bool handle windows.Handle flushHandle windows.Handle handles []windows.Handle } func newPoller(fd int) (*windowsPoller, error) { handle, err := windows.CreateEvent(nil, 0, 0, nil) if err != nil { return nil, err } flushHandle, err := windows.CreateEvent(nil, 0, 0, nil) if err != nil { windows.CloseHandle(handle) return nil, err } if err := efw.EbpfMapSetWaitHandle(fd, 0, handle); err != nil { windows.CloseHandle(handle) windows.CloseHandle(flushHandle) return nil, err } return &windowsPoller{ handle: handle, flushHandle: flushHandle, handles: []windows.Handle{handle, flushHandle}, }, nil } // Wait blocks until data is available or the deadline is reached. // Returns [os.ErrDeadlineExceeded] if a deadline was set and no wakeup was received. // Returns [ErrFlushed] if the ring buffer was flushed manually. // Returns [os.ErrClosed] if the poller was closed. func (p *windowsPoller) Wait(deadline time.Time) error { if p.closed.Load() { return os.ErrClosed } timeout := uint32(windows.INFINITE) if !deadline.IsZero() { timeout = uint32(internal.Between(time.Until(deadline).Milliseconds(), 0, windows.INFINITE-1)) } // Wait for either the ring buffer handle or the flush handle to be signaled result, err := windows.WaitForMultipleObjects(p.handles, false, timeout) switch result { case windows.WAIT_OBJECT_0: // Ring buffer event return nil case windows.WAIT_OBJECT_0 + 1: if p.closed.Load() { return os.ErrClosed } // Flush event return ErrFlushed case uint32(windows.WAIT_TIMEOUT): return os.ErrDeadlineExceeded case windows.WAIT_FAILED: return err default: return fmt.Errorf("unexpected wait result 0x%x: %w", result, err) } } // Flush interrupts [Wait] with [ErrFlushed]. func (p *windowsPoller) Flush() error { // Signal the handle to wake up any waiting threads if err := windows.SetEvent(p.flushHandle); err != nil { if errors.Is(err, windows.ERROR_INVALID_HANDLE) { return os.ErrClosed } return err } return nil } func (p *windowsPoller) Close() error { p.closed.Store(true) if err := p.Flush(); err != nil { return err } return errors.Join(windows.CloseHandle(p.handle), windows.CloseHandle(p.flushHandle)) } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/ring.go000066400000000000000000000055121520243672000221240ustar00rootroot00000000000000package ringbuf import ( "fmt" "io" "sync/atomic" "unsafe" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" ) type ringReader struct { // These point into mmap'ed memory and must be accessed atomically. prod_pos, cons_pos *uintptr mask uintptr ring []byte } func newRingReader(cons_ptr, prod_ptr *uintptr, ring []byte) *ringReader { return &ringReader{ prod_pos: prod_ptr, cons_pos: cons_ptr, // cap is always a power of two mask: uintptr(cap(ring)/2 - 1), ring: ring, } } // To be able to wrap around data, data pages in ring buffers are mapped twice in // a single contiguous virtual region. // Therefore the returned usable size is half the size of the mmaped region. func (rr *ringReader) size() int { return cap(rr.ring) / 2 } // The amount of data available to read in the ring buffer. func (rr *ringReader) AvailableBytes() uint64 { prod := atomic.LoadUintptr(rr.prod_pos) cons := atomic.LoadUintptr(rr.cons_pos) return uint64(prod - cons) } // Read a record from an event ring. func (rr *ringReader) readRecord(rec *Record) error { prod := atomic.LoadUintptr(rr.prod_pos) cons := atomic.LoadUintptr(rr.cons_pos) for { if remaining := prod - cons; remaining == 0 { return errEOR } else if remaining < sys.BPF_RINGBUF_HDR_SZ { return fmt.Errorf("read record header: %w", io.ErrUnexpectedEOF) } // read the len field of the header atomically to ensure a happens before // relationship with the xchg in the kernel. Without this we may see len // without BPF_RINGBUF_BUSY_BIT before the written data is visible. // See https://github.com/torvalds/linux/blob/v6.8/kernel/bpf/ringbuf.c#L484 start := cons & rr.mask len := atomic.LoadUint32((*uint32)((unsafe.Pointer)(&rr.ring[start]))) header := ringbufHeader{Len: len} if header.isBusy() { // the next sample in the ring is not committed yet so we // exit without storing the reader/consumer position // and start again from the same position. return errBusy } cons += sys.BPF_RINGBUF_HDR_SZ // Data is always padded to 8 byte alignment. dataLenAligned := uintptr(internal.Align(header.dataLen(), 8)) if remaining := prod - cons; remaining < dataLenAligned { return fmt.Errorf("read sample data: %w", io.ErrUnexpectedEOF) } start = cons & rr.mask cons += dataLenAligned if header.isDiscard() { // when the record header indicates that the data should be // discarded, we skip it by just updating the consumer position // to the next record. atomic.StoreUintptr(rr.cons_pos, cons) continue } if n := header.dataLen(); cap(rec.RawSample) < n { rec.RawSample = make([]byte, n) } else { rec.RawSample = rec.RawSample[:n] } copy(rec.RawSample, rr.ring[start:]) rec.Remaining = int(prod - cons) atomic.StoreUintptr(rr.cons_pos, cons) return nil } } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/ring_other.go000066400000000000000000000024371520243672000233300ustar00rootroot00000000000000//go:build !windows package ringbuf import ( "errors" "fmt" "os" "runtime" "unsafe" "github.com/cilium/ebpf/internal/unix" ) var _ eventRing = (*mmapEventRing)(nil) type mmapEventRing struct { prod []byte cons []byte *ringReader cleanup runtime.Cleanup } func newRingBufEventRing(mapFD, size int) (*mmapEventRing, error) { cons, err := unix.Mmap(mapFD, 0, os.Getpagesize(), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) if err != nil { return nil, fmt.Errorf("mmap consumer page: %w", err) } prod, err := unix.Mmap(mapFD, (int64)(os.Getpagesize()), os.Getpagesize()+2*size, unix.PROT_READ, unix.MAP_SHARED) if err != nil { _ = unix.Munmap(cons) return nil, fmt.Errorf("mmap data pages: %w", err) } cons_pos := (*uintptr)(unsafe.Pointer(&cons[0])) prod_pos := (*uintptr)(unsafe.Pointer(&prod[0])) ring := &mmapEventRing{ prod: prod, cons: cons, ringReader: newRingReader(cons_pos, prod_pos, prod[os.Getpagesize():]), } ring.cleanup = runtime.AddCleanup(ring, func(*byte) { _ = unix.Munmap(prod) _ = unix.Munmap(cons) }, nil) return ring, nil } func (ring *mmapEventRing) Close() error { ring.cleanup.Stop() prod, cons := ring.prod, ring.cons ring.prod, ring.cons = nil, nil return errors.Join( unix.Munmap(prod), unix.Munmap(cons), ) } golang-github-cilium-ebpf-0.21.0+ds1/ringbuf/ring_windows.go000066400000000000000000000031501520243672000236720ustar00rootroot00000000000000package ringbuf import ( "errors" "fmt" "runtime" "unsafe" "github.com/cilium/ebpf/internal/efw" "github.com/cilium/ebpf/internal/sys" ) var _ eventRing = (*windowsEventRing)(nil) type windowsEventRing struct { mapFd *sys.FD cons, prod, data *uint8 *ringReader cleanup runtime.Cleanup } func newRingBufEventRing(mapFD, size int) (*windowsEventRing, error) { dupFd, err := efw.EbpfDuplicateFd(mapFD) if err != nil { return nil, fmt.Errorf("duplicate map fd: %w", err) } fd, err := sys.NewFD(dupFd) if err != nil { _ = efw.EbpfCloseFd(dupFd) return nil, err } consPtr, prodPtr, dataPtr, dataLen, err := efw.EbpfRingBufferMapMapBuffer(dupFd) if err != nil { _ = fd.Close() return nil, fmt.Errorf("map consumer page: %w", err) } if dataLen != efw.Size(size) { _ = fd.Close() return nil, fmt.Errorf("map data length mismatch: %d != %d", dataLen, size) } // consPtr and prodPtr are guaranteed to be page size aligned. consPos := (*uintptr)(unsafe.Pointer(consPtr)) prodPos := (*uintptr)(unsafe.Pointer(prodPtr)) data := unsafe.Slice(dataPtr, dataLen*2) ring := &windowsEventRing{ mapFd: fd, cons: consPtr, prod: prodPtr, data: dataPtr, ringReader: newRingReader(consPos, prodPos, data), } ring.cleanup = runtime.AddCleanup(ring, func(*byte) { efw.EbpfRingBufferMapUnmapBuffer(fd.Int(), consPtr, prodPtr, dataPtr) }, nil) return ring, nil } func (ring *windowsEventRing) Close() error { ring.cleanup.Stop() return errors.Join( efw.EbpfRingBufferMapUnmapBuffer(ring.mapFd.Int(), ring.cons, ring.prod, ring.data), ring.mapFd.Close(), ) } golang-github-cilium-ebpf-0.21.0+ds1/rlimit/000077500000000000000000000000001520243672000204775ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/rlimit/doc.go000066400000000000000000000001401520243672000215660ustar00rootroot00000000000000// Package rlimit allows raising RLIMIT_MEMLOCK if necessary for the use of BPF. package rlimit golang-github-cilium-ebpf-0.21.0+ds1/rlimit/rlimit_linux.go000066400000000000000000000076751520243672000235640ustar00rootroot00000000000000package rlimit import ( "errors" "fmt" "sync" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/unix" ) var ( unsupportedMemcgAccounting = &internal.UnsupportedFeatureError{ MinimumVersion: internal.Version{5, 11, 0}, Name: "memcg-based accounting for BPF memory", } haveMemcgAccounting error rlimitMu sync.Mutex ) func init() { // We have to run this feature test at init, since it relies on changing // RLIMIT_MEMLOCK. Doing so is not safe in a concurrent program. Instead, // we rely on the initialization order guaranteed by the Go runtime to // execute the test in a safe environment: // // the invocation of init functions happens in a single goroutine, // sequentially, one package at a time. // // This is also the reason why RemoveMemlock is in its own package: // we only want to run the initializer if RemoveMemlock is called // from somewhere. haveMemcgAccounting = detectMemcgAccounting() } func detectMemcgAccounting() error { // Retrieve the original limit to prevent lowering Max, since // doing so is a permanent operation when running unprivileged. var oldLimit unix.Rlimit if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &oldLimit); err != nil { return fmt.Errorf("getting original memlock rlimit: %s", err) } // Drop the current limit to zero, maintaining the old Max value. // This is always permitted by the kernel for unprivileged users. // Retrieve a new copy of the old limit tuple to minimize the chances // of failing the restore operation below. zeroLimit := unix.Rlimit{Cur: 0, Max: oldLimit.Max} if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &zeroLimit, &oldLimit); err != nil { return fmt.Errorf("lowering memlock rlimit: %s", err) } attr := sys.MapCreateAttr{ MapType: 2, /* Array */ KeySize: 4, ValueSize: 4, MaxEntries: 1, } // Creating a map allocates shared (and locked) memory that counts against // the rlimit on pre-5.11 kernels, but against the memory cgroup budget on // kernels 5.11 and over. If this call succeeds with the process' memlock // rlimit set to 0, we can reasonably assume memcg accounting is supported. fd, mapErr := sys.MapCreate(&attr) // Restore old limits regardless of what happened. if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &oldLimit, nil); err != nil { return fmt.Errorf("restoring old memlock rlimit: %s", err) } // Map creation successful, memcg accounting supported. if mapErr == nil { fd.Close() return nil } // EPERM shows up when map creation would exceed the memory budget. if errors.Is(mapErr, unix.EPERM) { return unsupportedMemcgAccounting } // This shouldn't happen really. return fmt.Errorf("unexpected error detecting memory cgroup accounting: %s", mapErr) } // RemoveMemlock removes the limit on the amount of memory the current // process can lock into RAM, if necessary. // // This is not required to load eBPF resources on kernel versions 5.11+ // due to the introduction of cgroup-based memory accounting. On such kernels // the function is a no-op. // // Since the function may change global per-process limits it should be invoked // at program start up, in main() or init(). // // This function exists as a convenience and should only be used when // permanently raising RLIMIT_MEMLOCK to infinite is appropriate. Consider // invoking prlimit(2) directly with a more reasonable limit if desired. // // Requires CAP_SYS_RESOURCE on kernels < 5.11. func RemoveMemlock() error { if haveMemcgAccounting == nil { return nil } if !errors.Is(haveMemcgAccounting, unsupportedMemcgAccounting) { return haveMemcgAccounting } rlimitMu.Lock() defer rlimitMu.Unlock() // pid 0 affects the current process. Requires CAP_SYS_RESOURCE. newLimit := unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY} if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &newLimit, nil); err != nil { return fmt.Errorf("failed to set memlock rlimit: %w", err) } return nil } golang-github-cilium-ebpf-0.21.0+ds1/rlimit/rlimit_linux_test.go000066400000000000000000000017711520243672000246120ustar00rootroot00000000000000package rlimit import ( "testing" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/unix" "github.com/go-quicktest/qt" ) func TestRemoveMemlock(t *testing.T) { var before unix.Rlimit qt.Assert(t, qt.IsNil(unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &before))) err := RemoveMemlock() qt.Assert(t, qt.IsNil(err)) var after unix.Rlimit qt.Assert(t, qt.IsNil(unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &after))) // We can't use testutils here due to an import cycle. version, err := linux.KernelVersion() qt.Assert(t, qt.IsNil(err)) if version.Less(unsupportedMemcgAccounting.MinimumVersion) { qt.Assert(t, qt.Equals(after.Cur, unix.RLIM_INFINITY), qt.Commentf("cur should be INFINITY")) qt.Assert(t, qt.Equals(after.Max, unix.RLIM_INFINITY), qt.Commentf("max should be INFINITY")) } else { qt.Assert(t, qt.Equals(after.Cur, before.Cur), qt.Commentf("cur should be unchanged")) qt.Assert(t, qt.Equals(after.Max, before.Max), qt.Commentf("max should be unchanged")) } } golang-github-cilium-ebpf-0.21.0+ds1/rlimit/rlimit_other.go000066400000000000000000000002101520243672000235200ustar00rootroot00000000000000//go:build !linux package rlimit // RemoveMemlock is a no-op on platforms other than Linux. func RemoveMemlock() error { return nil } golang-github-cilium-ebpf-0.21.0+ds1/scripts/000077500000000000000000000000001520243672000206665ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/scripts/update-efw-deps.sh000077500000000000000000000012651520243672000242230ustar00rootroot00000000000000#!/bin/bash set -euo pipefail # Extract EFW version from CI workflow file efw_version=$(awk -F': ' '/CI_MAX_EFW_VERSION:/ {gsub(/['\''"]/, "", $2); print $2}' .github/workflows/ci.yml) if [ -z "$efw_version" ]; then echo "Error: Could not extract CI_MAX_EFW_VERSION from .github/workflows/ci.yml" >&2 exit 1 fi echo "Using EFW version: $efw_version" tmp=$(mktemp -d) cleanup() { rm -r "$tmp" } trap cleanup EXIT # Download and process ebpf_structs.h curl -fL "https://github.com/microsoft/ebpf-for-windows/raw/refs/tags/Release-v${efw_version}/include/ebpf_structs.h" -o "$tmp/ebpf_structs.h" "./internal/cmd/genwinfunctions.awk" "$tmp/ebpf_structs.h" | gofmt > "./asm/func_win.go" golang-github-cilium-ebpf-0.21.0+ds1/scripts/update-kernel-deps.sh000077500000000000000000000027061520243672000247230ustar00rootroot00000000000000#!/bin/bash set -euo pipefail # Extract kernel version from CI workflow file kernel_version=$(awk -F': ' '/CI_MAX_KERNEL_VERSION:/ {gsub(/['\''"]/, "", $2); print $2}' .github/workflows/ci.yml) if [ -z "$kernel_version" ]; then echo "Error: Could not extract CI_MAX_KERNEL_VERSION from .github/workflows/ci.yml" >&2 exit 1 fi echo "Using kernel version: $kernel_version" tmp=$(mktemp -d) cleanup() { rm -r "$tmp" } trap cleanup EXIT # Download and process libbpf.c # Truncate .0 patch versions (e.g., 6.16.0 -> 6.16, but leave 7.0 as 7.0) kernel_version_for_url="$kernel_version" if [[ $kernel_version =~ ^([0-9]+\.[0-9]+)\.0$ ]]; then kernel_version_for_url="${BASH_REMATCH[1]}" fi curl -fL "https://raw.githubusercontent.com/gregkh/linux/refs/tags/v$kernel_version_for_url/tools/lib/bpf/libbpf.c" -o "$tmp/libbpf.c" "./internal/cmd/gensections.awk" "$tmp/libbpf.c" | gofmt > "./elf_sections.go" # Download and process vmlinux and btf_testmod go tool crane export "ghcr.io/cilium/ci-kernels:$kernel_version" | tar -x -C "$tmp" extract-vmlinux "$tmp/boot/vmlinuz" > "$tmp/vmlinux" objcopy --dump-section .BTF=/dev/stdout "$tmp/vmlinux" /dev/null | gzip > "btf/testdata/vmlinux.btf.gz" find "$tmp/lib/modules" -type f -name bpf_testmod.ko -exec objcopy --dump-section .BTF="btf/testdata/btf_testmod.btf" {} /dev/null \; find "$tmp/lib/modules" -type f -name bpf_testmod.ko -exec objcopy --dump-section .BTF.base="btf/testdata/btf_testmod.btf.base" {} /dev/null \; golang-github-cilium-ebpf-0.21.0+ds1/scripts/windows/000077500000000000000000000000001520243672000223605ustar00rootroot00000000000000golang-github-cilium-ebpf-0.21.0+ds1/scripts/windows/README.md000066400000000000000000000016021520243672000236360ustar00rootroot00000000000000# Windows Development Setup You will need access to a Windows environment to work on ebpf-go for Windows. This repository contains a script which (mostly) automatically installs a Windows VM. It then proceeds to install dependencies necessary to compile and install eBPF for Windows. ```shell ./setup.sh path-to-windows.iso ``` Obtain the ISO by choosing "Download Windows 11 Disk Image (ISO)" on the [download page](https://www.microsoft.com/en-gb/software-download/windows11/) and then following the instructions. __Choose "English (United States)" as product language for a fully automated installation.__ ## SSH The setup script adds a public key from `~/.ssh`, you should be able to simply ssh into the VM by executing `ssh $IP`. ## Requirements * Only tested with Windows 11 * `libvirt` using qemu backend * `genisoimage` * `curl` * `envsubst` * `fzf` (optional, to select an ssh key) golang-github-cilium-ebpf-0.21.0+ds1/scripts/windows/Setup.ps1000066400000000000000000000050311520243672000241040ustar00rootroot00000000000000# Configure a fresh installation of Windows via SSH. param ( [switch] $RunOnce = $false ) if ($RunOnce) { # Visual Studio really doesn't seem to like being installed via SSH, so # we invoke from a RunOnce script. Invoke-WebRequest 'https://raw.githubusercontent.com/microsoft/ebpf-for-windows/main/scripts/Setup-DevEnv.ps1' -OutFile $env:TEMP\Setup-DevEnv.ps1 &"$env:TEMP\Setup-DevEnv.ps1" # sshd needs to be restarted to pick up new environment variables. Restart-Service sshd return } # Enable Developer Mode (so that symlinks work) # See https://stackoverflow.com/a/40033638 $RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" New-Item -Path $RegistryKeyPath -ItemType Directory -Force New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1 -Force # Ensure we have a PROFILE. # # This also allows chocolatey to add its hooks. if (!(Test-Path -Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force } # Add VS tools to PATH $addVsToolsToPath = @' # Add VS to PATH function Import-VsEnv { $vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" $vspath = & $vswherePath -property installationPath $vsDevShell = "${vspath}\Common7\Tools\Launch-VsDevShell.ps1" & $vsDevShell -SkipAutomaticLocation } '@ if (-not (Get-Content $PROFILE | Select-String "Add VS to PATH")) { $addVsToolsToPath | Add-Content -Path $PROFILE } # Enable git symlink support globally. $gitConfig = "${HOME}/.gitconfig" if (!(Test-Path -Path $gitConfig)) { New-Item -ItemType File -Path $gitConfig -Force } if (-not (Get-Content $gitConfig | Select-String "symlinks = true")) { @' [core] symlinks = true '@ | Add-Content -Path $gitConfig } # Install winget version which supports configure -f # if ([version]($(winget --version).substring(1)) -lt [version]'1.6.0') { # echo "Updating winget" # # From https://andrewstaylor.com/2023/11/28/winget-powershell-module/ # Get-PackageProvider NuGet -ForceBootstrap | Out-Null # install-module microsoft.winget.client -Force -AllowClobber # import-module microsoft.winget.client # repair-wingetpackagemanager -Force -Latest -AllUsers # } echo "Scheduling installation of eBPF for Windows dependencies for next reboot." Set-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce" -Name 'InstallEFWDependencies' -Value "powershell.exe -command `"start -verb runas powershell.exe -argumentlist \`"-file $PSCommandPath -RunOnce\`"" echo "Rebooting." Restart-Computer -Force golang-github-cilium-ebpf-0.21.0+ds1/scripts/windows/autounattend.xml000066400000000000000000000447751520243672000256360ustar00rootroot00000000000000 en-US 0409:00000409 en-US en-US en-US 0 3 2B87N-8KFHP-DKV6R-Y2C8J-PKCKT OnError true false 1 cmd.exe /c ">>"X:\diskpart.txt" (echo SELECT DISK=0&echo CLEAN&echo CONVERT GPT&echo CREATE PARTITION EFI SIZE=300&echo FORMAT QUICK FS=FAT32 LABEL="System"&echo CREATE PARTITION MSR SIZE=16)" 2 cmd.exe /c ">>"X:\diskpart.txt" (echo CREATE PARTITION PRIMARY&echo SHRINK MINIMUM=1000&echo FORMAT QUICK FS=NTFS LABEL="Windows"&echo CREATE PARTITION PRIMARY&echo FORMAT QUICK FS=NTFS LABEL="Recovery")" 3 cmd.exe /c ">>"X:\diskpart.txt" (echo SET ID="de94bba4-06d1-4d40-a16a-bfd50179d6ac"&echo GPT ATTRIBUTES=0x8000000000000001)" 4 cmd.exe /c "diskpart.exe /s "X:\diskpart.txt" >>"X:\diskpart.log" || ( type "X:\diskpart.log" & echo diskpart encountered an error. & pause & exit /b 1 )" 5 cmd.exe /c ">>"X:\defender.vbs" (echo WScript.Echo "Scanning for newly created SYSTEM registry hive file to disable Windows Defender services..."&echo Set fso = CreateObject("Scripting.FileSystemObject"^))" 6 cmd.exe /c ">>"X:\defender.vbs" (echo Set existing = CreateObject("Scripting.Dictionary"^)&echo Function Execute(command^)&echo WScript.Echo "Running command '" + command + "'"&echo Set shell = CreateObject("WScript.Shell"^))" 7 cmd.exe /c ">>"X:\defender.vbs" (echo Set exec = shell.Exec(command^)&echo Do While exec.Status = 0&echo WScript.Sleep 100&echo Loop&echo WScript.Echo exec.StdOut.ReadAll&echo WScript.Echo exec.StdErr.ReadAll)" 8 cmd.exe /c ">>"X:\defender.vbs" (echo Execute = exec.ExitCode&echo End Function&echo Function FindHiveFiles&echo Set FindHiveFiles = CreateObject("Scripting.Dictionary"^)&echo For Each drive In fso.Drives)" 9 cmd.exe /c ">>"X:\defender.vbs" (echo If drive.IsReady And drive.DriveLetter ^<^> "X" Then&echo For Each folder In Array("$Windows.~BT\NewOS\Windows", "Windows"^))" 10 cmd.exe /c ">>"X:\defender.vbs" (echo file = fso.BuildPath(fso.BuildPath(drive.RootFolder, folder^), "System32\config\SYSTEM"^)&echo If fso.FileExists(file^) And fso.FileExists(file + ".LOG1"^) And fso.FileExists(file + ".LOG2"^) Then)" 11 cmd.exe /c ">>"X:\defender.vbs" (echo FindHiveFiles.Add file, Nothing&echo End If&echo Next&echo End If&echo Next&echo End Function&echo For Each file In FindHiveFiles)" 12 cmd.exe /c ">>"X:\defender.vbs" (echo WScript.Echo "Will ignore file at '" + file + "' because it was already present when Windows Setup started."&echo existing.Add file, Nothing&echo Next&echo Do)" 13 cmd.exe /c ">>"X:\defender.vbs" (echo For Each file In FindHiveFiles&echo If Not existing.Exists(file^) Then&echo ret = 1&echo While ret ^> 0&echo WScript.Sleep 500&echo ret = Execute("reg.exe LOAD HKLM\mount " + file^))" 14 cmd.exe /c ">>"X:\defender.vbs" (echo Wend&echo For Each service In Array("Sense", "WdBoot", "WdFilter", "WdNisDrv", "WdNisSvc", "WinDefend"^))" 15 cmd.exe /c ">>"X:\defender.vbs" (echo ret = Execute("reg.exe ADD HKLM\mount\ControlSet001\Services\" + service + " /v Start /t REG_DWORD /d 4 /f"^)&echo Next&echo ret = Execute("reg.exe UNLOAD HKLM\mount"^))" 16 cmd.exe /c ">>"X:\defender.vbs" (echo WScript.Echo "Found and successfully modified SYSTEM registry hive file at '" + file + "'. This window will now close."&echo WScript.Sleep 5000&echo Exit Do&echo End If)" 17 cmd.exe /c ">>"X:\defender.vbs" (echo WScript.Sleep 1000&echo Next&echo Loop)" 18 cmd.exe /c "start /MIN cscript.exe //E:vbscript X:\defender.vbs" 1 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;" 2 powershell.exe -WindowStyle Normal -NoProfile -Command "Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\Specialize.ps1' -Raw | Invoke-Expression;" 3 reg.exe load "HKU\DefaultUser" "C:\Users\Default\NTUSER.DAT" 4 powershell.exe -WindowStyle Normal -NoProfile -Command "Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\DefaultUser.ps1' -Raw | Invoke-Expression;" 5 reg.exe unload "HKU\DefaultUser" 0409:00000409 en-US en-US en-US $USER Administrators true</PlainText> </Password> </LocalAccount> </LocalAccounts> </UserAccounts> <AutoLogon> <Username>$USER</Username> <Enabled>true</Enabled> <LogonCount>1</LogonCount> <Password> <Value/> <PlainText>true</PlainText> </Password> </AutoLogon> <OOBE> <ProtectYourPC>3</ProtectYourPC> <HideEULAPage>true</HideEULAPage> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <HideOnlineAccountScreens>false</HideOnlineAccountScreens> </OOBE> <FirstLogonCommands> <SynchronousCommand wcm:action="add"> <Order>1</Order> <CommandLine>powershell.exe -WindowStyle Normal -NoProfile -Command "Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\FirstLogon.ps1' -Raw | Invoke-Expression;"</CommandLine> </SynchronousCommand> </FirstLogonCommands> </component> </settings> <Extensions xmlns="https://schneegans.de/windows/unattend-generator/"> <ExtractScript> param( [xml] $Document ); foreach( $file in $Document.unattend.Extensions.File ) { $path = [System.Environment]::ExpandEnvironmentVariables( $file.GetAttribute( 'path' ) ); mkdir -Path( $path | Split-Path -Parent ) -ErrorAction 'SilentlyContinue'; $encoding = switch( [System.IO.Path]::GetExtension( $path ) ) { { $_ -in '.ps1', '.xml' } { [System.Text.Encoding]::UTF8; } { $_ -in '.reg', '.vbs', '.js' } { [System.Text.UnicodeEncoding]::new( $false, $true ); } default { [System.Text.Encoding]::Default; } }; $bytes = $encoding.GetPreamble() + $encoding.GetBytes( $file.InnerText.Trim() ); [System.IO.File]::WriteAllBytes( $path, $bytes ); } </ExtractScript> <File path="C:\Windows\Setup\Scripts\VirtIoGuestTools.ps1"> &amp; { foreach( $letter in 'DEFGHIJKLMNOPQRSTUVWXYZ'.ToCharArray() ) { $exe = "${letter}:\virtio-win-guest-tools.exe"; if( Test-Path -LiteralPath $exe ) { Start-Process -FilePath $exe -ArgumentList '/passive', '/norestart' -Wait; return; } } 'VirtIO Guest Tools image (virtio-win-*.iso) is not attached to this VM.'; } *&gt;&amp;1 &gt;&gt; 'C:\Windows\Setup\Scripts\VirtIoGuestTools.log'; </File> <File path="C:\Windows\Setup\Scripts\unattend-01.ps1"> bcdedit.exe -set TESTSIGNING ON; </File> <File path="C:\Windows\Setup\Scripts\unattend-02.ps1"> # https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0; Start-Service sshd; Set-Service -Name sshd -StartupType 'Automatic'; New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force; icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"; # Allow inbound connections Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultInboundAction Allow; # Set up authorized keys echo "$AUTHORIZED_KEYS" | Out-File -Encoding utf8 -FilePath "C:\ProgramData\ssh\administrators_authorized_keys" </File> <File path="C:\Windows\Setup\Scripts\Specialize.ps1"> $scripts = @( { net.exe accounts /lockoutthreshold:0; }; { net.exe accounts /maxpwage:UNLIMITED; }; { reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender Security Center\Notifications" /v DisableNotifications /t REG_DWORD /d 1 /f; }; { Set-ExecutionPolicy -Scope 'LocalMachine' -ExecutionPolicy 'RemoteSigned' -Force; }; { reg.exe add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t REG_DWORD /d 1 /f; }; { Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\VirtIoGuestTools.ps1' -Raw | Invoke-Expression; }; { reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\BitLocker" /v "PreventDeviceEncryption" /t REG_DWORD /d 1 /f; }; { reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Edge" /v HideFirstRunExperience /t REG_DWORD /d 1 /f; }; { Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\unattend-01.ps1' -Raw | Invoke-Expression; }; ); &amp; { [float] $complete = 0; [float] $increment = 100 / $scripts.Count; foreach( $script in $scripts ) { Write-Progress -Activity 'Running scripts to customize your Windows installation. Do not close this window.' -PercentComplete $complete; '*** Will now execute command «{0}».' -f $( $str = $script.ToString().Trim() -replace '\s+', ' '; $max = 100; if( $str.Length -le $max ) { $str; } else { $str.Substring( 0, $max - 1 ) + '…'; } ); $start = [datetime]::Now; &amp; $script; '*** Finished executing command after {0:0} ms.' -f [datetime]::Now.Subtract( $start ).TotalMilliseconds; "`r`n" * 3; $complete += $increment; } } *&gt;&amp;1 &gt;&gt; "C:\Windows\Setup\Scripts\Specialize.log"; </File> <File path="C:\Windows\Setup\Scripts\DefaultUser.ps1"> $scripts = @( { $names = @( 'ContentDeliveryAllowed'; 'FeatureManagementEnabled'; 'OEMPreInstalledAppsEnabled'; 'PreInstalledAppsEnabled'; 'PreInstalledAppsEverEnabled'; 'SilentInstalledAppsEnabled'; 'SoftLandingEnabled'; 'SubscribedContentEnabled'; 'SubscribedContent-310093Enabled'; 'SubscribedContent-338387Enabled'; 'SubscribedContent-338388Enabled'; 'SubscribedContent-338389Enabled'; 'SubscribedContent-338393Enabled'; 'SubscribedContent-353698Enabled'; 'SystemPaneSuggestionsEnabled'; ); foreach( $name in $names ) { reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v $name /t REG_DWORD /d 0 /f; } }; ); &amp; { [float] $complete = 0; [float] $increment = 100 / $scripts.Count; foreach( $script in $scripts ) { Write-Progress -Activity 'Running scripts to modify the default user’’s registry hive. Do not close this window.' -PercentComplete $complete; '*** Will now execute command «{0}».' -f $( $str = $script.ToString().Trim() -replace '\s+', ' '; $max = 100; if( $str.Length -le $max ) { $str; } else { $str.Substring( 0, $max - 1 ) + '…'; } ); $start = [datetime]::Now; &amp; $script; '*** Finished executing command after {0:0} ms.' -f [datetime]::Now.Subtract( $start ).TotalMilliseconds; "`r`n" * 3; $complete += $increment; } } *&gt;&amp;1 &gt;&gt; "C:\Windows\Setup\Scripts\DefaultUser.log"; </File> <File path="C:\Windows\Setup\Scripts\FirstLogon.ps1"> $scripts = @( { Set-ItemProperty -LiteralPath 'Registry::HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name 'AutoLogonCount' -Type 'DWord' -Force -Value 0; }; { Disable-ComputerRestore -Drive 'C:\'; }; { Get-Content -LiteralPath 'C:\Windows\Setup\Scripts\unattend-02.ps1' -Raw | Invoke-Expression; }; ); &amp; { [float] $complete = 0; [float] $increment = 100 / $scripts.Count; foreach( $script in $scripts ) { Write-Progress -Activity 'Running scripts to finalize your Windows installation. Do not close this window.' -PercentComplete $complete; '*** Will now execute command «{0}».' -f $( $str = $script.ToString().Trim() -replace '\s+', ' '; $max = 100; if( $str.Length -le $max ) { $str; } else { $str.Substring( 0, $max - 1 ) + '…'; } ); $start = [datetime]::Now; &amp; $script; '*** Finished executing command after {0:0} ms.' -f [datetime]::Now.Subtract( $start ).TotalMilliseconds; "`r`n" * 3; $complete += $increment; } } *&gt;&amp;1 &gt;&gt; "C:\Windows\Setup\Scripts\FirstLogon.log"; </File> </Extensions> </unattend>���golang-github-cilium-ebpf-0.21.0+ds1/scripts/windows/setup-efw.sh�����������������������������������0000775�0000000�0000000�00000000654�15202436720�0024643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env bash # Install dependencies required by eBPF for Windows. set -euo pipefail VM_NAME="$1" ip=$(virsh --connect qemu:///system domifaddr "$VM_NAME" | gawk 'match($0, /([[:digit:]\.]+)\//, a) { print a[1] }') if [ -z "$ip" ]; then echo "Can't figure out IP address of VM, giving up" exit 1 fi echo "VM IP is $ip" echo Installing eBPF for Windows dependencies scp ./*.ps1 "$ip": ssh -t "$ip" ".\\Setup.ps1" ������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/scripts/windows/setup.sh���������������������������������������0000775�0000000�0000000�00000010132�15202436720�0024054�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env bash set -euo pipefail # Variables VIRTIO_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" VIRTIO_ISO="/tmp/virtio-win.iso" # Check if ISO path is provided if [ -z "$1" ]; then echo "Usage: $0 <path_to_windows_iso>" exit 1 else ISO_PATH=$1 fi if [ "$(whoami)" = "root" ]; then echo "Do not run this script as root: it prevents detecting the correct user name" exit 1 fi # Prompt settings read -p "Enter the name of the VM (default is vm): " VM_NAME VM_NAME=${VM_NAME:-vm} read -p "Enter the amount of RAM in MB (default is 8192): " RAM_MB RAM_MB=${RAM_MB:-8192} read -p "Enter the disk size in GB (default is 100): " DISK_SIZE DISK_SIZE=${DISK_SIZE:-100} SSH_PUBKEYS=("$HOME/.ssh"/*.pub) if [ ${#SSH_PUBKEYS[@]} -eq 0 ]; then echo "No .pub files found in ~/.ssh directory." exit 1 elif [ ${#SSH_PUBKEYS[@]} -eq 1 ]; then SSH_PUBKEY=${SSH_PUBKEYS[0]} else SSH_PUBKEY=$(printf "%s\n" "${SSH_PUBKEYS[@]}" | fzf --prompt="Select a .pub file: ") fi if [ -z "$SSH_PUBKEY" ]; then echo "No SSH pubkey selected." exit 1 fi # Check disk before starting download VM_DISK="/var/lib/libvirt/images/${VM_NAME}.qcow2" if [ -f "$VM_DISK" ]; then echo "Error: $VM_DISK already exists" exit 1 fi # Download Virtio Drivers ISO echo "Downloading Virtio drivers ISO..." curl -L -o "$VIRTIO_ISO" --etag-save "$VIRTIO_ISO.tmp" --etag-compare "$VIRTIO_ISO.etag" "$VIRTIO_ISO_URL" mv "$VIRTIO_ISO.tmp" "$VIRTIO_ISO.etag" # Create autounattend temp="$(mktemp -d)" cleanup() { sudo umount "$temp/mount" 2> /dev/null rm -rf "$temp" } trap cleanup EXIT chmod 0755 "$temp" mkdir -p "$temp/mount" "$temp/modifications" # Prepare an installation file automatically installs Windows. # # Allows ssh authentication with all public keys found in ~/.ssh. AUTHORIZED_KEYS="$(cat "$SSH_PUBKEY")" envsubst '$USER $AUTHORIZED_KEYS' < autounattend.xml > "$temp/modifications/autounattend.xml" # Generate bootable ISO. # # This ISO contains the autounattend.xml and doesn't require pressing a button # to start the installation. See: # * https://palant.info/2023/02/13/automating-windows-installation-in-a-vm/ sudo mount -o loop "$ISO_PATH" "$temp/mount" genisoimage \ -iso-level 4 -rock -udf \ -disable-deep-relocation \ -untranslated-filenames \ -allow-limited-size \ -no-emul-boot \ -boot-load-size 8 \ -eltorito-boot boot/etfsboot.com \ -eltorito-alt-boot \ -eltorito-boot efi/microsoft/boot/efisys_noprompt.bin \ -o "$temp/win.iso" \ "$temp/mount" "$temp/modifications" # Create VM Disk sudo qemu-img create -f qcow2 "$VM_DISK" "${DISK_SIZE}G" # Define and create the VM using virt-install. # This will start the VM. sudo virt-install \ --connect qemu:///system \ --name "$VM_NAME" \ --ram "$RAM_MB" \ --vcpus "$(nproc),cores=$(nproc)" \ --cpu "host-passthrough,check=none,migratable=off,feature.vmx=require,feature.hle=disable,feature.rtm=disable" \ --os-variant win11 \ --network network=default,model=e1000 \ --channel type=unix,source.mode=bind,target.type=virtio,target.name=org.qemu.guest_agent.0 \ --graphics spice \ --disk path="$VM_DISK",format=qcow2,bus=sata,size="$DISK_SIZE",boot.order=1 \ --disk path="$temp/win.iso",device=cdrom,bus=sata,boot.order=2 \ --disk path="$VIRTIO_ISO",device=cdrom,bus=sata \ --install bootdev=cdrom \ --boot uefi,firmware.feature0.name=enrolled-keys,firmware.feature0.enabled=no \ --noautoconsole echo "Windows VM setup initiated, click through the installer." echo "You may have to manually start the VM a couple of times." # Show the graphical output so that the user can follow along. virt-manager --connect qemu:///system --show-domain-console "$VM_NAME" echo "Waiting for VM to receive an IP." ip="" while [ -z "$ip" ]; do sleep 10 ip="$(virsh --connect qemu:///system domifaddr "$VM_NAME" | gawk 'match($0, /([[:digit:]\.]+)\//, a) { print a[1] }')" echo -n . done echo echo "Waiting for SSH to become available to continue installation." while ! ssh -o ConnectTimeout=10 -T "$ip" '$true' &> /dev/null; do echo -n . done echo ./setup-efw.sh "$VM_NAME" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/struct_ops.go��������������������������������������������������0000664�0000000�0000000�00000012333�15202436720�0021735�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "errors" "fmt" "reflect" "strings" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" ) const structOpsValuePrefix = "bpf_struct_ops_" const structOpsLinkSec = ".struct_ops.link" const structOpsSec = ".struct_ops" const structOpsKeySize = 4 // structOpsFindInnerType returns the "inner" struct inside a value struct_ops type. // // Given a value like: // // struct bpf_struct_ops_bpf_testmod_ops { // struct bpf_struct_ops_common common; // struct bpf_testmod_ops data; // }; // // this function returns the *btf.Struct for "bpf_testmod_ops" along with the // byte offset of the "data" member inside the value type. // // The inner struct name is derived by trimming the "bpf_struct_ops_" prefix // from the value's name. func structOpsFindInnerType(vType *btf.Struct) (*btf.Struct, uint32, error) { innerName := strings.TrimPrefix(vType.Name, structOpsValuePrefix) for _, m := range vType.Members { if st, ok := btf.As[*btf.Struct](m.Type); ok && st.Name == innerName { return st, m.Offset.Bytes(), nil } } return nil, 0, fmt.Errorf("inner struct %q not found in %s", innerName, vType.Name) } // structOpsFindTarget resolves the kernel-side "value struct" for a struct_ops map. func structOpsFindTarget(userType *btf.Struct, cache *btf.Cache) (vType *btf.Struct, id btf.TypeID, module *btf.Handle, err error) { // the kernel value type name, e.g. "bpf_struct_ops_<name>" vTypeName := structOpsValuePrefix + userType.Name target := btf.Type((*btf.Struct)(nil)) spec, module, err := findTargetInKernel(vTypeName, &target, cache) if errors.Is(err, btf.ErrNotFound) { return nil, 0, nil, fmt.Errorf("%q doesn't exist in kernel: %w", vTypeName, ErrNotSupported) } if err != nil { return nil, 0, nil, fmt.Errorf("lookup value type %q: %w", vTypeName, err) } id, err = spec.TypeID(target) if err != nil { return nil, 0, nil, err } return target.(*btf.Struct), id, module, nil } // structOpsPopulateValue writes a `prog FD` which references to `p` into the // struct_ops value buffer `kernVData` at byte offset `dstOff` corresponding to // the member `km`. func structOpsPopulateValue(km btf.Member, kernVData []byte, p *Program) error { kmPtr, ok := btf.As[*btf.Pointer](km.Type) if !ok { return fmt.Errorf("member %s is not a func pointer", km.Name) } if _, isFuncProto := btf.As[*btf.FuncProto](kmPtr.Target); !isFuncProto { return fmt.Errorf("member %s is not a func pointer", km.Name) } dstOff := int(km.Offset.Bytes()) if dstOff < 0 || dstOff+8 > len(kernVData) { return fmt.Errorf("member %q: value buffer too small for func ptr", km.Name) } internal.NativeEndian.PutUint64(kernVData[dstOff:dstOff+8], uint64(p.FD())) return nil } // structOpsCopyMember copies a single member from the user struct (m) // into the kernel value struct (km) for struct_ops. func structOpsCopyMember(m, km btf.Member, data []byte, kernVData []byte) error { mSize, err := btf.Sizeof(m.Type) if err != nil { return fmt.Errorf("sizeof(user.%s): %w", m.Name, err) } kSize, err := btf.Sizeof(km.Type) if err != nil { return fmt.Errorf("sizeof(kernel.%s): %w", km.Name, err) } if mSize != kSize { return fmt.Errorf("size mismatch for %s: user=%d kernel=%d", m.Name, mSize, kSize) } if km.BitfieldSize > 0 || m.BitfieldSize > 0 { return fmt.Errorf("bitfield %s not supported", m.Name) } srcOff := int(m.Offset.Bytes()) dstOff := int(km.Offset.Bytes()) if srcOff < 0 || srcOff+mSize > len(data) { return fmt.Errorf("member %q: userdata is too small", m.Name) } if dstOff < 0 || dstOff+mSize > len(kernVData) { return fmt.Errorf("member %q: value type is too small", m.Name) } // skip mods(const, restrict, volatile and typetag) // and typedef to check type compatibility mType := btf.UnderlyingType(m.Type) kernMType := btf.UnderlyingType(km.Type) if reflect.TypeOf(mType) != reflect.TypeOf(kernMType) { return fmt.Errorf("unmatched member type %s != %s (kernel)", m.Name, km.Name) } switch mType.(type) { case *btf.Struct, *btf.Union: if !structOpsIsMemZeroed(data[srcOff : srcOff+mSize]) { return fmt.Errorf("non-zero nested struct %s: %w", m.Name, ErrNotSupported) } // the bytes has zeroed value, we simply skip the copy. return nil } copy(kernVData[dstOff:dstOff+mSize], data[srcOff:srcOff+mSize]) return nil } // structOpsIsMemZeroed() checks whether all bytes in data are zero. func structOpsIsMemZeroed(data []byte) bool { for _, b := range data { if b != 0 { return false } } return true } // structOpsSetAttachTo sets p.AttachTo in the expected "struct_name:memberName" format // based on the struct definition. // // this relies on the assumption that each member in the // `.struct_ops` section has a relocation at its starting byte offset. func structOpsSetAttachTo( sec *elfSection, baseOff uint32, userSt *btf.Struct, progs map[string]*ProgramSpec) error { for _, m := range userSt.Members { memberOff := m.Offset sym, ok := sec.relocations[uint64(baseOff+memberOff.Bytes())] if !ok { continue } p, ok := progs[sym.Name] if !ok || p == nil { return fmt.Errorf("program %s not found", sym.Name) } if p.Type != StructOps { return fmt.Errorf("program %s is not StructOps", sym.Name) } p.AttachTo = userSt.Name + ":" + m.Name } return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/struct_ops_test.go���������������������������������������������0000664�0000000�0000000�00000001316�15202436720�0022773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "testing" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/testutils" ) func TestCreateStructOpsMapSpecSimple(t *testing.T) { requireTestmodOps(t) ms := &MapSpec{ Name: "testmod_ops", Type: StructOpsMap, Flags: sys.BPF_F_LINK, Key: &btf.Int{Size: 4}, KeySize: 4, Value: &btf.Struct{Name: "bpf_testmod_ops"}, MaxEntries: 1, Contents: []MapKV{ { Key: uint32(0), Value: make([]byte, 448), }, }, } m, err := NewMap(ms) testutils.SkipIfNotSupported(t, err) if err != nil { t.Fatalf("creating struct_ops map failed: %v", err) } t.Cleanup(func() { _ = m.Close() }) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/syscalls.go����������������������������������������������������0000664�0000000�0000000�00000020553�15202436720�0021370�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "bytes" "errors" "fmt" "math" "os" "runtime" "strings" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/linux" "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" "github.com/cilium/ebpf/internal/tracefs" "github.com/cilium/ebpf/internal/unix" ) var ( // pre-allocating these here since they may // get called in hot code paths and cause // unnecessary memory allocations sysErrKeyNotExist = sys.Error(ErrKeyNotExist, unix.ENOENT) sysErrKeyExist = sys.Error(ErrKeyExist, unix.EEXIST) sysErrNotSupported = sys.Error(ErrNotSupported, sys.ENOTSUPP) ) // sanitizeName replaces all invalid characters in name with replacement. // Passing a negative value for replacement will delete characters instead // of replacing them. // // The set of allowed characters may change over time. func sanitizeName(name string, replacement rune) string { return strings.Map(func(char rune) rune { switch { case char >= 'A' && char <= 'Z': return char case char >= 'a' && char <= 'z': return char case char >= '0' && char <= '9': return char case char == '.': return char case char == '_': return char default: return replacement } }, name) } func maybeFillObjName(name string) sys.ObjName { if errors.Is(haveObjName(), ErrNotSupported) { return sys.ObjName{} } name = sanitizeName(name, -1) if errors.Is(objNameAllowsDot(), ErrNotSupported) { name = strings.ReplaceAll(name, ".", "") } return sys.NewObjName(name) } func progLoad(insns asm.Instructions, typ ProgramType, license string) (*sys.FD, error) { buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) if err := insns.Marshal(buf, internal.NativeEndian); err != nil { return nil, err } bytecode := buf.Bytes() return sys.ProgLoad(&sys.ProgLoadAttr{ ProgType: sys.ProgType(typ), License: sys.NewStringPointer(license), Insns: sys.SlicePointer(bytecode), InsnCnt: uint32(len(bytecode) / asm.InstructionSize), }) } var haveNestedMaps = internal.NewFeatureTest("nested maps", func() error { if platform.IsWindows { // We only support efW versions which have this feature, no need to probe. return nil } _, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(ArrayOfMaps), KeySize: 4, ValueSize: 4, MaxEntries: 1, // Invalid file descriptor. InnerMapFd: ^uint32(0), }) if errors.Is(err, unix.EINVAL) { return internal.ErrNotSupported } if errors.Is(err, unix.EBADF) { return nil } return err }, "4.12", "windows:0.21.0") var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only maps", func() error { // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: sys.BPF_F_RDONLY_PROG, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }, "5.2") var haveMmapableMaps = internal.NewFeatureTest("mmapable maps", func() error { // This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: sys.BPF_F_MMAPABLE, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }, "5.5") var haveInnerMaps = internal.NewFeatureTest("inner maps", func() error { // This checks BPF_F_INNER_MAP, which appeared in 5.10. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: sys.BPF_F_INNER_MAP, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }, "5.10") var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", func() error { // This checks BPF_F_NO_PREALLOC, which appeared in 4.6. m, err := sys.MapCreate(&sys.MapCreateAttr{ MapType: sys.MapType(Hash), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapFlags: sys.BPF_F_NO_PREALLOC, }) if err != nil { return internal.ErrNotSupported } _ = m.Close() return nil }, "4.6") func wrapMapError(err error) error { if err == nil { return nil } if errors.Is(err, unix.ENOENT) { return sysErrKeyNotExist } if errors.Is(err, unix.EEXIST) { return sysErrKeyExist } if errors.Is(err, sys.ENOTSUPP) { return sysErrNotSupported } if errors.Is(err, unix.E2BIG) { return fmt.Errorf("key too big for map: %w", err) } return err } var haveObjName = internal.NewFeatureTest("object names", func() error { if platform.IsWindows { // We only support efW versions which have this feature, no need to probe. return nil } attr := sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapName: sys.NewObjName("feature_test"), } fd, err := sys.MapCreate(&attr) if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }, "4.15", "windows:0.21.0") var objNameAllowsDot = internal.NewFeatureTest("dot in object names", func() error { if platform.IsWindows { // We only support efW versions which have this feature, no need to probe. return nil } if err := haveObjName(); err != nil { return err } attr := sys.MapCreateAttr{ MapType: sys.MapType(Array), KeySize: 4, ValueSize: 4, MaxEntries: 1, MapName: sys.NewObjName(".test"), } fd, err := sys.MapCreate(&attr) if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }, "5.2", "windows:0.21.0") var haveBatchAPI = internal.NewFeatureTest("map batch api", func() error { var maxEntries uint32 = 2 attr := sys.MapCreateAttr{ MapType: sys.MapType(Hash), KeySize: 4, ValueSize: 4, MaxEntries: maxEntries, } fd, err := sys.MapCreate(&attr) if err != nil { return internal.ErrNotSupported } defer fd.Close() keys := []uint32{1, 2} values := []uint32{3, 4} kp, _ := marshalMapSyscallInput(keys, 8) vp, _ := marshalMapSyscallInput(values, 8) err = sys.MapUpdateBatch(&sys.MapUpdateBatchAttr{ MapFd: fd.Uint(), Keys: kp, Values: vp, Count: maxEntries, }) if err != nil { return internal.ErrNotSupported } return nil }, "5.6") var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", func() error { insns := asm.Instructions{ asm.Mov.Reg(asm.R1, asm.R10), asm.Add.Imm(asm.R1, -8), asm.Mov.Imm(asm.R2, 8), asm.Mov.Imm(asm.R3, 0), asm.FnProbeReadKernel.Call(), asm.Return(), } fd, err := progLoad(insns, Kprobe, "GPL") if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }, "5.5") var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", func() error { insns := asm.Instructions{ asm.Call.Label("prog2").WithSymbol("prog1"), asm.Return(), asm.Mov.Imm(asm.R0, 0).WithSymbol("prog2"), asm.Return(), } fd, err := progLoad(insns, SocketFilter, "MIT") if err != nil { return internal.ErrNotSupported } _ = fd.Close() return nil }, "4.16") var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", func() error { prefix := linux.PlatformPrefix() if prefix == "" { return fmt.Errorf("unable to find the platform prefix for (%s)", runtime.GOARCH) } args := tracefs.ProbeArgs{ Type: tracefs.Kprobe, Symbol: prefix + "sys_bpf", Pid: -1, } var err error args.Group, err = tracefs.RandomGroup("ebpf_probe") if err != nil { return err } evt, err := tracefs.NewEvent(args) if errors.Is(err, os.ErrNotExist) { return internal.ErrNotSupported } if err != nil { return err } return evt.Close() }, "4.17") var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", func() error { insns := asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), } buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) if err := insns.Marshal(buf, internal.NativeEndian); err != nil { return err } bytecode := buf.Bytes() _, err := sys.ProgLoad(&sys.ProgLoadAttr{ ProgType: sys.ProgType(SocketFilter), License: sys.NewStringPointer("MIT"), Insns: sys.SlicePointer(bytecode), InsnCnt: uint32(len(bytecode) / asm.InstructionSize), FuncInfoCnt: 1, ProgBtfFd: math.MaxUint32, }) if errors.Is(err, unix.EBADF) { return nil } if errors.Is(err, unix.E2BIG) { return ErrNotSupported } return err }, "5.0") �����������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/syscalls_test.go�����������������������������������������������0000664�0000000�0000000�00000002761�15202436720�0022430�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "testing" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/internal/testutils" ) func TestSanitizeName(t *testing.T) { for input, want := range map[string]string{ "test": "test", "": "", "a-b": "ab", "yeah so": "yeahso", "dot.": "dot.", "Capital": "Capital", "t_est": "t_est", "hörnchen": "hrnchen", } { qt.Assert(t, qt.Equals(sanitizeName(input, -1), want), qt.Commentf("input: %s", input)) } } func TestHaveBatchAPI(t *testing.T) { testutils.CheckFeatureTest(t, haveBatchAPI) } func TestHaveObjName(t *testing.T) { testutils.CheckFeatureTest(t, haveObjName) } func TestObjNameAllowsDot(t *testing.T) { testutils.CheckFeatureTest(t, objNameAllowsDot) } func TestHaveNestedMaps(t *testing.T) { testutils.CheckFeatureTest(t, haveNestedMaps) } func TestHaveMapMutabilityModifiers(t *testing.T) { testutils.CheckFeatureTest(t, haveMapMutabilityModifiers) } func TestHaveMmapableMaps(t *testing.T) { testutils.CheckFeatureTest(t, haveMmapableMaps) } func TestHaveInnerMaps(t *testing.T) { testutils.CheckFeatureTest(t, haveInnerMaps) } func TestHaveProbeReadKernel(t *testing.T) { testutils.CheckFeatureTest(t, haveProbeReadKernel) } func TestHaveBPFToBPFCalls(t *testing.T) { testutils.CheckFeatureTest(t, haveBPFToBPFCalls) } func TestHaveSyscallWrapper(t *testing.T) { testutils.CheckFeatureTest(t, haveSyscallWrapper) } func TestHaveProgramExtInfos(t *testing.T) { testutils.CheckFeatureTest(t, haveProgramExtInfos) } ���������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/������������������������������������������������������0000775�0000000�0000000�00000000000�15202436720�0021010�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/arena-eb.elf������������������������������������������0000664�0000000�0000000�00000002140�15202436720�0023147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������`�����@�����@�������������������������������������������������Z��������������������� ��������������������!������������ ������������������������������������������������������������d���������������������������� ���)����������.������@���8���������D��� ������N������ ������T��������� ������� �int�__ARRAY_SIZE_TYPE__�__unique_value0�type�map_flags�max_entries�map_extra�arena�.maps����������������������������������+����������������� �������������.text�.maps�.llvm_addrsig�.strtab�.symtab�arena�.rel.BTF��������������������������������������������������������������������������������������������������!�������:���������������������������������������������������@�����������������������������������������������������������@������� ��������������������������5��������������������������`������z��������������������������1��� �������@�������������������������������������������� oL��������������������� ���������������������������������#��������������������������������0����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/arena-el.elf������������������������������������������0000664�0000000�0000000�00000002140�15202436720�0023161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������`����������@�����@�����������������������������������������������Z���������������������� �������������������!������������ ������������������������������������������������������������d���������������������������� ���)����������.������@���8���������D��� ������N������ ������T��������� ������� ����int�__ARRAY_SIZE_TYPE__�__unique_value0�type�map_flags�max_entries�map_extra�arena�.maps�������������������������������+������������� ��������������������.text�.maps�.llvm_addrsig�.strtab�.symtab�arena�.rel.BTF��������������������������������������������������������������������������������������������!������:���������������������������������������������������@�����������������������������������������������������������@������� ������������������������������5����������������������`�������z�����������������������������1��� ���@������������������������������������������������ ���Lo��������������� ������������������������������������#����������������������������0�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/arena.c�����������������������������������������������0000664�0000000�0000000�00000000473�15202436720�0022246�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" struct { __uint(type, BPF_MAP_TYPE_ARENA); __uint(map_flags, BPF_F_MMAPABLE); __uint(max_entries, 100); /* number of pages */ __ulong(map_extra, 0x1ull << 44); /* start of mmap region */ } arena __section(".maps"); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/btf_map_init-eb.elf�����������������������������������0000664�0000000�0000000�00000005340�15202436720�0024521�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������`�����@�����@��������*������� ��������������0����������� ������������������������������������������������������������������������������������������������������������������������������������������������������������ ��������������������� �������������������������������� ����������������������"��������� ���������� ������������������������������ ���� ��������������������������� ���������������� ���/����������4������@���8���������>���������J��� ������Q������ ������������������������������������������ ���/����������>������@���4���������8���������a��������������������������������������� ���������������������������������������� ���/����������>������@���4���������8���������J���������k������������� ���������z �������������������� �������������� �����������������������0���������� ����������0�int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�type�key�value�max_entries�values�prog_array_init�inner_map�outer_map_init�tail_1�socket/tail�/ebpf/testdata/btf_map_init.c� return 42;�ctx�tail_main�socket/main� bpf_tail_call(ctx, &prog_array_init, 1);� return 0;�.maps����� �������$���$���D���h������������������������������������������������������������������������p��� ��������x�������������������������������������������������������������������������������������������G�����������������0��������������������0���=���������0������� ���$���������P�������0���������������������(�������������x�������������������������������������������������,�������������<�������������P�������������h�������������x������ �.text�.rel.BTF.ext�prog_array_init�outer_map_init�.rel.maps�inner_map�tail_main�.relsocket/main�socket/tail�.llvm_addrsig�.strtab�.symtab�.rel.BTF�tail_1��������������������������������������������������������������������{�����������������������������������������������������������������������������������@����������������������������������a�������������������������@���������������������������������U�������������������������P�������0��������������������������Q��� �������@������������������������ ��������������������7����������������������������������������������������������3��� �������@�������������� ������� ��� ��������������������������������������������������������������������������������� �������@��������������@�������0��� �������������������� ������������������������������������������������������������� �������@��������������p�������P��� ��� �����������������moL�������������������������������������������������������������������������������P���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/btf_map_init-el.elf�����������������������������������0000664�0000000�0000000�00000005340�15202436720�0024533�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������`����������@�����@������*�������������������������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������������������� ������������������������������� ����������������������"��������� ���������� ������������������������������ ��������� ���������������������� ���������������� ���/����������4������@���8���������>���������J��� ������Q������ ������������������������������������������ ���/����������>������@���4���������8���������a��������������������������������������� ���������������������������������������� ���/����������>������@���4���������8���������J���������k������������������� ���z����� �������������������� �������������� ������������������0���������� ����������0����int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�type�key�value�max_entries�values�prog_array_init�inner_map�outer_map_init�tail_1�socket/tail�/ebpf/testdata/btf_map_init.c� return 42;�ctx�tail_main�socket/main� bpf_tail_call(ctx, &prog_array_init, 1);� return 0;�.maps�� �������$���$���D���h�������������������������������������������������������������������������p�� ���������x������������������������������������������������������������������������������������������G�������������0��������������������0�������=�����0������� �������$�����P�������0���������������������(�������������x�������������������������������������������������,�������������<�������������P�������������h�������������x������������� �.text�.rel.BTF.ext�prog_array_init�outer_map_init�.rel.maps�inner_map�tail_main�.relsocket/main�socket/tail�.llvm_addrsig�.strtab�.symtab�.rel.BTF�tail_1�����������������������������������������������������������������{�������������������������������������������������������������������������������@��������������������������������������a���������������������@�������������������������������������U���������������������P�������0������������������������������Q��� ���@���������������������������� ��������������������7����������������������������������������������������������3��� ���@��������������� ������ ������� ��������������������������������������������������������������������������������� ���@���������������@������0������� �������������������� ������������������������������������������������������������� ���@���������������p������P������� ��� �����������������m���Lo�������������������������������������������������������������������������P���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/btf_map_init.c����������������������������������������0000664�0000000�0000000�00000002422�15202436720�0023607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" int __section("socket/tail") tail_1() { return 42; } // Tail call map (program array) initialized with program pointers. struct { __uint(type, BPF_MAP_TYPE_PROG_ARRAY); __type(key, uint32_t); __type(value, uint32_t); __uint(max_entries, 2); __array(values, int()); } prog_array_init __section(".maps") = { .values = { // Skip index 0 to exercise empty array slots. [1] = &tail_1, }, }; int __section("socket/main") tail_main(void *ctx) { // If prog_array_init is correctly populated, the tail call // will succeed and the program will continue in tail_1 and // not return here. bpf_tail_call(ctx, &prog_array_init, 1); return 0; } // Inner map with a single possible entry. struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 1); __type(key, uint32_t); __type(value, uint32_t); } inner_map __section(".maps"); // Outer map carrying a reference to the inner map. struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __uint(max_entries, 2); __type(key, uint32_t); __type(value, uint32_t); __array(values, typeof(inner_map)); } outer_map_init __section(".maps") = { .values = { // Skip index 0 to exercise empty array slots. [1] = &inner_map, }, }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/common.h����������������������������������������������0000664�0000000�0000000�00000004661�15202436720�0022460�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once typedef _Bool bool; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef signed int int32_t; typedef unsigned long uint64_t; enum libbpf_tristate { TRI_NO = 0, TRI_YES = 1, TRI_MODULE = 2, }; #define ___bpf_concat(a, b) ____bpf_concat(a, b) #define ____bpf_concat(a, b) a ## b #define __section(NAME) __attribute__((section(NAME), used)) #define __uint(name, val) int(*name)[val] #define __type(name, val) typeof(val) *name #define __array(name, val) typeof(val) *name[] #define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name #define __kconfig __attribute__((section(".kconfig"))) #define __ksym __attribute__((section(".ksyms"))) #define __noinline __attribute__((noinline)) #define __weak __attribute__((weak)) #define __hidden __attribute__((visibility("hidden"))) #define bpf_ksym_exists(sym) \ ({ \ _Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \ !!sym; \ }) #define core_access __builtin_preserve_access_index #define BPF_MAP_TYPE_HASH (1) #define BPF_MAP_TYPE_ARRAY (2) #define BPF_MAP_TYPE_PROG_ARRAY (3) #define BPF_MAP_TYPE_PERF_EVENT_ARRAY (4) #define BPF_MAP_TYPE_ARRAY_OF_MAPS (12) #define BPF_MAP_TYPE_HASH_OF_MAPS (13) #define BPF_MAP_TYPE_ARENA (33) #define BPF_F_NO_PREALLOC (1U << 0) #define BPF_F_MMAPABLE (1U << 10) #define BPF_F_CURRENT_CPU (0xffffffffULL) /* From tools/lib/bpf/libbpf.h */ struct bpf_map_def { unsigned int type; unsigned int key_size; unsigned int value_size; unsigned int max_entries; unsigned int map_flags; }; static void *(*bpf_map_lookup_elem)(const void *map, const void *key) = (void *)1; static long (*bpf_map_update_elem)(const void *map, const void *key, const void *value, uint64_t flags) = (void *)2; static long (*bpf_trace_printk)(const char *fmt, uint32_t fmt_size, ...) = (void *)6; static uint32_t (*bpf_get_smp_processor_id)(void) = (void *)8; static long (*bpf_tail_call)(void *ctx, void *prog_array_map, uint32_t index) = (void *)12; static int (*bpf_perf_event_output)(const void *ctx, const void *map, uint64_t index, const void *data, uint64_t size) = (void *)25; static void *(*bpf_get_current_task)() = (void *)35; static long (*bpf_probe_read_kernel)(void *dst, uint32_t size, const void *unsafe_ptr) = (void *)113; static long (*bpf_for_each_map_elem)(const void *map, void *callback_fn, void *callback_ctx, uint64_t flags) = (void *)164; �������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/constants-eb.elf��������������������������������������0000664�0000000�0000000�00000003350�15202436720�0024101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@� ���������������a�������������MIT���������������������� ����������������� ��� ��������F�������������������������������K��������� ���_������������� ������ ���� ������ ���i������ ���r��������� ������������������������ ���������������������������int�freeze_rodata�sk_lookup/�/ebpf/testdata/constants.c� return ret;�char�__ARRAY_SIZE_TYPE__�__license�uint32_t�unsigned int�ret�.rodata�license������ �������������,���@���������������������������������������9��< ���������9��<�������������������������������������������������@����������������� �����������������������&����������������������������������������������������������������,�������������@�������������P������ �.text�.rel.BTF.ext�ret�.llvm_addrsig�__license�.strtab�.symtab�freeze_rodata�.rodata�.rel.BTF�.relsk_lookup/���������������������������������������������������������������������������0�������������������������3�������n���������������������������������������������������@����������������������������������c�������������������������@������� ��������������������������_��� �������@������������������������ ��������������������(�������������������������`���������������������������������N�������������������������d���������������������������������Z��������������������������h��������������������������������V��� �������@��������������������� ��� �������������������� ��������������������������������`����������������������������� �������@����������������������0��� ��� �����������������oL���������������������0���������������������������������8�������������������������X�������x������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/constants-el.elf��������������������������������������0000664�0000000�0000000�00000003350�15202436720�0024113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@� ����������������a�������������MIT������������������������� ������������ ������� ���F�������������������������������K��������� ���_������������������� ���������� ���i������ ���r��������� ������������������������ ������������������������������int�freeze_rodata�sk_lookup/�/ebpf/testdata/constants.c� return ret;�char�__ARRAY_SIZE_TYPE__�__license�uint32_t�unsigned int�ret�.rodata�license��� �������������,���@���������������������������������������9��� <��������9���<������������������������������������������������@������������� ���������������������������&������������������������������������������������������������,�������������@�������������P������������� �.text�.rel.BTF.ext�ret�.llvm_addrsig�__license�.strtab�.symtab�freeze_rodata�.rodata�.rel.BTF�.relsk_lookup/������������������������������������������������������������������������0����������������������3������n���������������������������������������������������@��������������������������������������c���������������������@������� ������������������������������_��� ���@���������������������������� ��������������������(���������������������`�������������������������������������N���������������������d�������������������������������������Z����������������������h������������������������������������V��� ���@��������������������� ������� �������������������� ����������������������������`��������������������������������� ���@����������������������0������� ��� ��������������������Lo���������������0������������������������������������8����������������������X������x�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/constants.c�������������������������������������������0000664�0000000�0000000�00000001053�15202436720�0023167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file exercises the ELF loader. It is not a valid BPF program. */ #include "common.h" char __license[] __section("license") = "MIT"; /* * Maps with the Freeze flag set (like .rodata) must be frozen before sending * programs to the verifier so constants can be used during verification. If * done incorrectly, the following sk_lookup program will fail to verify since * the only valid return code is 1. See bpf/verifier.c:check_return_code(). */ volatile const uint32_t ret = -1; __section("sk_lookup/") int freeze_rodata() { return ret; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/�����������������������������������������������0000775�0000000�0000000�00000000000�15202436720�0022257�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/Dockerfile�������������������������������������0000664�0000000�0000000�00000002103�15202436720�0024245�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This Dockerfile generates a build environment for generating ELFs # of testdata programs. Run `make build` in this directory to build it. FROM golang:1.25-bookworm COPY llvm-snapshot.gpg.key . RUN apt-get update && \ apt-get -y --no-install-recommends install ca-certificates gnupg && \ apt-key add llvm-snapshot.gpg.key && \ rm llvm-snapshot.gpg.key && \ apt-get remove -y gnupg && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* COPY llvm.list /etc/apt/sources.list.d RUN apt-get update && \ apt-get -y --no-install-recommends install \ make git gawk \ libbpf-dev \ bpftool \ clang-format \ clang-14 llvm-14 \ clang-17 llvm-17 \ clang-20 llvm-20 && \ rm -rf /var/lib/apt/lists/* # Examples use `#include <asm/types.h>` which Debian carries in asm-generic/ instead. RUN ln -s /usr/include/asm-generic /usr/include/asm RUN curl -fL "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/scripts/extract-vmlinux" -o "/usr/local/bin/extract-vmlinux" && \ chmod +x /usr/local/bin/extract-vmlinux �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/IMAGE������������������������������������������0000664�0000000�0000000�00000000034�15202436720�0023021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ghcr.io/cilium/ebpf-builder ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/Makefile���������������������������������������0000664�0000000�0000000�00000000636�15202436720�0023724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Makefile to build and push the `cilium/ebpf` llvm builder Docker image. CONTAINER_ENGINE ?= docker IMAGE := $(shell cat IMAGE) EPOCH := $(shell date +'%s') ifndef IMAGE $(error IMAGE file not present in Makefile directory) endif .PHONY: build push build: ${CONTAINER_ENGINE} build --no-cache . -t "$(IMAGE):$(EPOCH)" echo $(EPOCH) > VERSION push: ${CONTAINER_ENGINE} push "$(IMAGE):$(shell cat VERSION)" ��������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/README.md��������������������������������������0000664�0000000�0000000�00000002227�15202436720�0023541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# `cilium/ebpf` LLVM Builder Image This is a simple Docker image to provide reproducible eBPF ELF builds across contributors' workstations. This standardizes on a single environment used to regenerate e.g. testdata ELFs and does not depend on the toolchain installed on the host machine. Additionally, it reduces drift in the bytecode committed to the repository over time as the same exact clang + llc version is used throughout the development lifecycle. Only when upgrading or rebuilding the Docker image would changes in .elf files be expected (assuming the .c files are untouched). ## Building Building the image requires Docker. Run the build with: `make build` This updates the `VERSION` file. Commit it and submit a PR upstream. ### Regeneration Testdata on non-x86 platforms Before running `make`, ensure [Docker buildx](https://docs.docker.com/buildx/working-with-buildx/) is enabled. Additionally `QEMU user` and `binfmt` should be installed. On a Debian based distribution the command to add them is `apt install qemu-user-static binfmt-support`. ## Pushing After building, push the image to the Docker registry specified in `IMAGE` with: `make push` �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/VERSION����������������������������������������0000664�0000000�0000000�00000000013�15202436720�0023321�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������1768997810 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/llvm-snapshot.gpg.key��������������������������0000664�0000000�0000000�00000006111�15202436720�0026353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) mQINBFE9lCwBEADi0WUAApM/mgHJRU8lVkkw0CHsZNpqaQDNaHefD6Rw3S4LxNmM EZaOTkhP200XZM8lVdbfUW9xSjA3oPldc1HG26NjbqqCmWpdo2fb+r7VmU2dq3NM R18ZlKixiLDE6OUfaXWKamZsXb6ITTYmgTO6orQWYrnW6ckYHSeaAkW0wkDAryl2 B5v8aoFnQ1rFiVEMo4NGzw4UX+MelF7rxaaregmKVTPiqCOSPJ1McC1dHFN533FY Wh/RVLKWo6npu+owtwYFQW+zyQhKzSIMvNujFRzhIxzxR9Gn87MoLAyfgKEzrbbT DhqqNXTxS4UMUKCQaO93TzetX/EBrRpJj+vP640yio80h4Dr5pAd7+LnKwgpTDk1 G88bBXJAcPZnTSKu9I2c6KY4iRNbvRz4i+ZdwwZtdW4nSdl2792L7Sl7Nc44uLL/ ZqkKDXEBF6lsX5XpABwyK89S/SbHOytXv9o4puv+65Ac5/UShspQTMSKGZgvDauU cs8kE1U9dPOqVNCYq9Nfwinkf6RxV1k1+gwtclxQuY7UpKXP0hNAXjAiA5KS5Crq 7aaJg9q2F4bub0mNU6n7UI6vXguF2n4SEtzPRk6RP+4TiT3bZUsmr+1ktogyOJCc Ha8G5VdL+NBIYQthOcieYCBnTeIH7D3Sp6FYQTYtVbKFzmMK+36ERreL/wARAQAB tD1TeWx2ZXN0cmUgTGVkcnUgLSBEZWJpYW4gTExWTSBwYWNrYWdlcyA8c3lsdmVz dHJlQGRlYmlhbi5vcmc+iQI4BBMBAgAiBQJRPZQsAhsDBgsJCAcDAgYVCAIJCgsE FgIDAQIeAQIXgAAKCRAVz00Yr090Ibx+EADArS/hvkDF8juWMXxh17CgR0WZlHCC 9CTBWkg5a0bNN/3bb97cPQt/vIKWjQtkQpav6/5JTVCSx2riL4FHYhH0iuo4iAPR udC7Cvg8g7bSPrKO6tenQZNvQm+tUmBHgFiMBJi92AjZ/Qn1Shg7p9ITivFxpLyX wpmnF1OKyI2Kof2rm4BFwfSWuf8Fvh7kDMRLHv+MlnK/7j/BNpKdozXxLcwoFBmn l0WjpAH3OFF7Pvm1LJdf1DjWKH0Dc3sc6zxtmBR/KHHg6kK4BGQNnFKujcP7TVdv gMYv84kun14pnwjZcqOtN3UJtcx22880DOQzinoMs3Q4w4o05oIF+sSgHViFpc3W R0v+RllnH05vKZo+LDzc83DQVrdwliV12eHxrMQ8UYg88zCbF/cHHnlzZWAJgftg hB08v1BKPgYRUzwJ6VdVqXYcZWEaUJmQAPuAALyZESw94hSo28FAn0/gzEc5uOYx K+xG/lFwgAGYNb3uGM5m0P6LVTfdg6vDwwOeTNIExVk3KVFXeSQef2ZMkhwA7wya KJptkb62wBHFE+o9TUdtMCY6qONxMMdwioRE5BYNwAsS1PnRD2+jtlI0DzvKHt7B MWd8hnoUKhMeZ9TNmo+8CpsAtXZcBho0zPGz/R8NlJhAWpdAZ1CmcPo83EW86Yq7 BxQUKnNHcwj2ebkCDQRRPZQsARAA4jxYmbTHwmMjqSizlMJYNuGOpIidEdx9zQ5g zOr431/VfWq4S+VhMDhs15j9lyml0y4ok215VRFwrAREDg6UPMr7ajLmBQGau0Fc bvZJ90l4NjXp5p0NEE/qOb9UEHT7EGkEhaZ1ekkWFTWCgsy7rRXfZLxB6sk7pzLC DshyW3zjIakWAnpQ5j5obiDy708pReAuGB94NSyb1HoW/xGsGgvvCw4r0w3xPStw F1PhmScE6NTBIfLliea3pl8vhKPlCh54Hk7I8QGjo1ETlRP4Qll1ZxHJ8u25f/ta RES2Aw8Hi7j0EVcZ6MT9JWTI83yUcnUlZPZS2HyeWcUj+8nUC8W4N8An+aNps9l/ 21inIl2TbGo3Yn1JQLnA1YCoGwC34g8QZTJhElEQBN0X29ayWW6OdFx8MDvllbBV ymmKq2lK1U55mQTfDli7S3vfGz9Gp/oQwZ8bQpOeUkc5hbZszYwP4RX+68xDPfn+ M9udl+qW9wu+LyePbW6HX90LmkhNkkY2ZzUPRPDHZANU5btaPXc2H7edX4y4maQa xenqD0lGh9LGz/mps4HEZtCI5CY8o0uCMF3lT0XfXhuLksr7Pxv57yue8LLTItOJ d9Hmzp9G97SRYYeqU+8lyNXtU2PdrLLq7QHkzrsloG78lCpQcalHGACJzrlUWVP/ fN3Ht3kAEQEAAYkCHwQYAQIACQUCUT2ULAIbDAAKCRAVz00Yr090IbhWEADbr50X OEXMIMGRLe+YMjeMX9NG4jxs0jZaWHc/WrGR+CCSUb9r6aPXeLo+45949uEfdSsB pbaEdNWxF5Vr1CSjuO5siIlgDjmT655voXo67xVpEN4HhMrxugDJfCa6z97P0+ML PdDxim57uNqkam9XIq9hKQaurxMAECDPmlEXI4QT3eu5qw5/knMzDMZj4Vi6hovL wvvAeLHO/jsyfIdNmhBGU2RWCEZ9uo/MeerPHtRPfg74g+9PPfP6nyHD2Wes6yGd oVQwtPNAQD6Cj7EaA2xdZYLJ7/jW6yiPu98FFWP74FN2dlyEA2uVziLsfBrgpS4l tVOlrO2YzkkqUGrybzbLpj6eeHx+Cd7wcjI8CalsqtL6cG8cUEjtWQUHyTbQWAgG 5VPEgIAVhJ6RTZ26i/G+4J8neKyRs4vz+57UGwY6zI4AB1ZcWGEE3Bf+CDEDgmnP LSwbnHefK9IljT9XU98PelSryUO/5UPw7leE0akXKB4DtekToO226px1VnGp3Bov 1GBGvpHvL2WizEwdk+nfk8LtrLzej+9FtIcq3uIrYnsac47Pf7p0otcFeTJTjSq3 krCaoG4Hx0zGQG2ZFpHrSrZTVy6lxvIdfi0beMgY6h78p6M9eYZHQHc02DjFkQXN bXb5c6gCHESH5PXwPU4jQEE7Ib9J6sbk7ZT2Mw== =j+4q -----END PGP PUBLIC KEY BLOCK----- �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/docker/llvm.list��������������������������������������0000664�0000000�0000000�00000000670�15202436720�0024131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Taken from https://apt.llvm.org. deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main deb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-17 main deb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-17 main deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-20 main deb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-20 main ������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/errors-eb.elf�����������������������������������������0000664�0000000�0000000�00000004370�15202436720�0023404�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������8�����@�����@� ���������������������������������a�����������������������������������������������������������������������������������;���� ����������������� ��� ��������n��������w����������� ��������� ����������������������� ��������� ��������� ��������& ����� ��4��������� ���������int�poisoned_single�socket�/ebpf/testdata/errors.c�__section("socket") int poisoned_single() {�0� FORCE_BTF;�nonexist�non_exist�0:0� return core_access(ne.non_exist);�}�poisoned_double�__section("socket") int poisoned_double() {�nonexist_enum�NON_EXIST�poisoned_kfunc� return invalid_kfunc();�invalid_kfunc�.ksyms������ �������$���$���������L�������������������@������x��� ��������� ����������4��D����������b��H���0��������P���8��������T���@��������\����X������b��`���p��������h���x����� �� �������� ��������������������`������0�������������H������`������`������`��� �����������������������������������������������������-�����������������@���=���������@�������8���[���������x����������M ��������������������������x������ ��������������������,�������������4�������������<�������������P�������������`�������������p�������������������������������������������������������������������������������������������������������������������� ������������������ �.text�.rel.BTF.ext�.relsocket�.llvm_addrsig�poisoned_single�poisoned_double�invalid_kfunc�poisoned_kfunc�.strtab�.symtab�.rel.BTF����������������������������������������������������������������������j�����������������������������������������������������������������������������������@�����������������������������������������������������������@������������������������������������ �������@������������������������ ��������������������~����������������������������������������������������������z��� �������@������������������������ �������������������� �������������������������������,����������������������������� �������@������������������������ ��������������������oL������������������������������������������������������r�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/errors-el.elf�����������������������������������������0000664�0000000�0000000�00000004370�15202436720�0023416�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������8����������@�����@� ����������������������������������a���������������������������������������������������������������������������������;��������� ������������ ������� ���n��������w����������������� �������� ������������������������ �������� ���������� ���&���� ���4�������� ������������int�poisoned_single�socket�/ebpf/testdata/errors.c�__section("socket") int poisoned_single() {�0� FORCE_BTF;�nonexist�non_exist�0:0� return core_access(ne.non_exist);�}�poisoned_double�__section("socket") int poisoned_double() {�nonexist_enum�NON_EXIST�poisoned_kfunc� return invalid_kfunc();�invalid_kfunc�.ksyms��� �������$���$���������L�������������������@������x��� ��������� ����������4����D��������b���H��0���������P��8���������T��@����������\��X������b���`��p���������h��x������ �� �������� �������������������`������0�������������H������`������`������`��� �����������������������������������������������������-�������������@�������=�����@�������8�������[�����x��������������M��� �������������������x������� �������������������,�������������4�������������<�������������P�������������`�������������p��������������������������������������������������������������������������������������������������������������������� ������������������������ �.text�.rel.BTF.ext�.relsocket�.llvm_addrsig�poisoned_single�poisoned_double�invalid_kfunc�poisoned_kfunc�.strtab�.symtab�.rel.BTF�������������������������������������������������������������������j�������������������������������������������������������������������������������@�����������������������������������������������������������@���������������������������������������� ���@���������������������������� ��������������������~����������������������������������������������������������z��� ���@���������������������������� �������������������� ����������������������������,�������������������������������� ���@���������������������������� �����������������������Lo���������������������������������������������������r��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/errors.c����������������������������������������������0000664�0000000�0000000�00000001454�15202436720�0022474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" #include "../btf/testdata/bpf_core_read.h" struct nonexist { int non_exist; }; enum nonexist_enum { NON_EXIST = 1 }; // Force loading program with BTF by including a relocation for a local type. #define FORCE_BTF \ do { \ if (bpf_core_type_id_local(int) == 0) \ return __LINE__; \ } while (0) __section("socket") int poisoned_single() { FORCE_BTF; struct nonexist ne; return core_access(ne.non_exist); } __section("socket") int poisoned_double() { FORCE_BTF; return bpf_core_enum_value(enum nonexist_enum, NON_EXIST); } extern int invalid_kfunc(void) __ksym __weak; __section("socket") int poisoned_kfunc() { // NB: This doesn't go via CO-RE but uses a similar mechanism to generate // an invalid instruction. We test it here for convenience. return invalid_kfunc(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/fentry_fexit-eb.elf�����������������������������������0000664�0000000�0000000�00000003610�15202436720�0024572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������H�����@�����@� �������������������������������������������Dual MIT/GPL������������������������� ����������������� ��� ��������� ���������K ��������� ���������f ��������p������������������������� ��� ���u��������� ��������� ��������������� ������� �int�trace_on_entry�fentry/target�/ebpf/testdata/fentry_fexit.c� return 0;�trace_on_exit�fexit/target�target�tc�char�__ARRAY_SIZE_TYPE__�__license�license������ �������4���4���L��������������������������Y�������������m��������������������������"���@�����Y����������"���@��(���m����������"���@��8���������������������������������������������������������������������������������������������������������������������#��������������������E��������������������Z����������������� ��������������������,�������������<�������������L�������������`�������������x������������������� �trace_on_entry�.text�.rel.BTF.ext�trace_on_exit�fentry/target�fexit/target�.llvm_addrsig�__license�tc�.strtab�.symtab�.rel.BTF������������������������������������������������������������������������g�����������������������������������������������������������������������������������@����������������������������������1�������������������������@���������������������������������?�������������������������P���������������������������������d�������������������������`���������������������������������\�������������������������p������� ��������������������������{��������������������������������k��������������������������w��� �������@��������������P���������� ��������������������������������������������������������������������������������� �������@��������������`�������`��� ��� �����������������LoL������������������������������������������������������o����������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/fentry_fexit-el.elf�����������������������������������0000664�0000000�0000000�00000003610�15202436720�0024604�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������H����������@�����@� ��������������������������������������������Dual MIT/GPL���������������������������� ������������ ������� ���������� ���K����� ���������� ���f����� ���p������������������������� ��� ���u��������� ��������� ��������������� ������� ����int�trace_on_entry�fentry/target�/ebpf/testdata/fentry_fexit.c� return 0;�trace_on_exit�fexit/target�target�tc�char�__ARRAY_SIZE_TYPE__�__license�license��� �������4���4���L��������������������������Y�������������m��������������������������"���@�����Y����������"���@���(��m����������"���@���8��������������������������������������������������������������������������������������������������������������������#��������������������E��������������������Z������������� ��������������������,�������������<�������������L�������������`�������������x�������������������������� �trace_on_entry�.text�.rel.BTF.ext�trace_on_exit�fentry/target�fexit/target�.llvm_addrsig�__license�tc�.strtab�.symtab�.rel.BTF���������������������������������������������������������������������g�������������������������������������������������������������������������������@��������������������������������������1���������������������@�������������������������������������?���������������������P�������������������������������������d���������������������`�������������������������������������\���������������������p������� ������������������������������{�����������������������������k�����������������������������w��� ���@���������������P������������� ��������������������������������������������������������������������������������� ���@���������������`������`������� ��� �����������������L���Lo���������������������������������������������������o�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/fentry_fexit.c����������������������������������������0000664�0000000�0000000�00000000370�15202436720�0023662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; __section("fentry/target") int trace_on_entry() { return 0; } __section("fexit/target") int trace_on_exit() { return 0; } __section("tc") int target() { return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/freplace-eb.elf���������������������������������������0000664�0000000�0000000�00000004410�15202436720�0023644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@� �������c����a ����������������������������������MIT���������������?���� ����������������� ��� ������������������{�������������������������������������@����������������� ���������������� ���� �������������� ����� ���� ��������� ����� ��(������������������������� �����-�����������7�����������������int�subprog�.text�/ebpf/testdata/freplace.c�__attribute__((noinline)) int subprog() {� volatile int ret = 0;� return ret;�bpf_args�args�uint64_t�unsigned long�__ARRAY_SIZE_TYPE__�ctx�sched_process_exec�raw_tracepoint/sched_process_exec� return subprog();�replacement�freplace/subprog� return 0;�char�__license�license������ �������4���4���|������������� ����������������������� �� ���������� ������ �������������-��,����������W��0���������n��4������������������D �����������D�� ��������������T�������������������������������������������������������������������������������������������������)����������������� ���\����������������������������������������?������������������������������� ������(�������������,�������������<�������������L�������������`�������������p�����������������������������������������������������������.text�.rel.BTF.ext�replacement�freplace/subprog�.llvm_addrsig�__license�.relraw_tracepoint/sched_process_exec�.strtab�.symtab�.rel.BTF�������������������������������������������������������������������������o�������������������������;����������������������������������������������������������@������� ��������������������������M�������������������������`���������������������������������I��� �������@������������������������ �������������������� �������������������������p���������������������������������A������������������������������������������������������������������������������������������o����������������������������� �������@������������������������ �������������������� ������������������������������������������������������������� �������@������������������������ ��� �����������������1oL���������������������8���������������������������������w������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/freplace-el.elf���������������������������������������0000664�0000000�0000000�00000004410�15202436720�0023656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@� ��������c����a����������������������������������MIT�������������?��������� ������������ ������� �������������{�������������������������������������@����������������� ���������������� ��������� �������������� ���������� �������� ���(������������������������ ������-�����������7�������������������int�subprog�.text�/ebpf/testdata/freplace.c�__attribute__((noinline)) int subprog() {� volatile int ret = 0;� return ret;�bpf_args�args�uint64_t�unsigned long�__ARRAY_SIZE_TYPE__�ctx�sched_process_exec�raw_tracepoint/sched_process_exec� return subprog();�replacement�freplace/subprog� return 0;�char�__license�license��� �������4���4���|������������� ����������������������� ��� ��������� ������ �������������-����,��������W���0��������n���4������������������ D�����������D�� ��������������T������������������������������������������������������������������������������������������������)������������� �������\����������������������������������������?���������������������������� ������(������������,�������������<�������������L�������������`�������������p������������������������������������������������������������������.text�.rel.BTF.ext�replacement�freplace/subprog�.llvm_addrsig�__license�.relraw_tracepoint/sched_process_exec�.strtab�.symtab�.rel.BTF����������������������������������������������������������������������o����������������������;���������������������������������������������������������@������� ������������������������������M���������������������`�������������������������������������I��� ���@���������������������������� �������������������� ���������������������p�������������������������������������A���������������������������������������������������������������������������������������o�������������������������������� ���@���������������������������� �������������������� ������������������������������������������������������������� ���@���������������������������� ��� �����������������1���Lo���������������8������������������������������������w���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/freplace.c��������������������������������������������0000664�0000000�0000000�00000000633�15202436720�0022737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// /* This file excercises freplace. */ #include "common.h" char __license[] __section("license") = "MIT"; struct bpf_args { uint64_t args[0]; }; __attribute__((noinline)) int subprog() { volatile int ret = 0; return ret; } __section("raw_tracepoint/sched_process_exec") int sched_process_exec(struct bpf_args *ctx) { return subprog(); } __section("freplace/subprog") int replacement() { return 0; } �����������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/fwd_decl-eb.elf���������������������������������������0000664�0000000�0000000�00000002310�15202436720�0023627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������H�����@�����@� ���������������������H���H���B���� ����������������� ��� ��������� ����������������> ������int�call_fwd�socket�/ebpf/testdata/fwd_decl.c� return fwd();�fwd������� �������������,���@���������������������������������������/��$ ���������/��$�����������������������������������������������������-��������������������2��������������������������������� �������,�������������@�������������P������ �.text�.rel.BTF.ext�.relsocket�.llvm_addrsig�call_fwd�.strtab�.symtab�.BTF������������������������������������������������������������������������6��������������������������������K���������������������������������������������������@�����������������������������������������������������������@������������������������������������ �������@������������������������ ��������������������F��������������������������P��������������������������������� ���������������������������������`����������������������������� �������@���������������������0��� ��������������������oL������������������������������������������������������>�������������������������X�������`��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/fwd_decl-el.elf���������������������������������������0000664�0000000�0000000�00000002310�15202436720�0023641�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������H����������@�����@� �������������������H���H���B���������� ������������ ������� ��������� �����������>����� ����int�call_fwd�socket�/ebpf/testdata/fwd_decl.c� return fwd();�fwd���� �������������,���@���������������������������������������/��� $��������/���$����������������������������������������������������-��������������������2������������������������������ ������,�������������@�������������P������������� �.text�.rel.BTF.ext�.relsocket�.llvm_addrsig�call_fwd�.strtab�.symtab�.BTF���������������������������������������������������������������������6����������������������������K���������������������������������������������������@�����������������������������������������������������������@���������������������������������������� ���@���������������������������� ��������������������F����������������������P������������������������������������� �����������������������������`��������������������������������� ���@���������������������0������� �����������������������Lo���������������������������������������������������>����������������������X������`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/fwd_decl.c��������������������������������������������0000664�0000000�0000000�00000000326�15202436720�0022724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" // Forward function declaration, never implemented. int fwd(); __section("socket") int call_fwd() { return fwd(); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid-kfunc-eb.elf����������������������������������0000664�0000000�0000000�00000003250�15202436720�0024616�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@� ����������������Dual MIT/GPL������������������������� ����������������� ��� ��������� ����������c ������������������������������������ ������������ �������������������������������������������� ������� �int�call_kfunc�tc�/ebpf/testdata/invalid-kfunc.c� bpf_kfunc_call_test_mem_len_pass1();� return 1;�bpf_kfunc_call_test_mem_len_pass1�char�__ARRAY_SIZE_TYPE__�__license�.ksyms�license������ �������������,���@���������������������������������������2��$���������X��(�������������������������������������������������3��������������������W����������������������"����������������� �������������� ���������������������������������,�������������@�������������P������ �.text�.rel.BTF.ext�.llvm_addrsig�__license�.reltc�call_kfunc�.strtab�.symtab�.rel.BTF�bpf_kfunc_call_test_mem_len_pass1�������������������������������������������������������������������������>�������������������������*�������y���������������������������������������������������@����������������������������������0�������������������������@���������������������������������,��� �������@������������������������ ��������������������$�������������������������X������� ��������������������������R��������������������������h��������������������������������N��� �������@��������������������� ��� �������������������� ��������������������������������`����������������������������� �������@���������������������0��� ��������������������oL���������������������(���������������������������������F�������������������������P�������x����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid-kfunc-el.elf����������������������������������0000664�0000000�0000000�00000003250�15202436720�0024630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@� �����������������Dual MIT/GPL���������������������������� ������������ ������� ���������� ����c����� ������������������������������� ������������ �������������������������������������������� ������� ����int�call_kfunc�tc�/ebpf/testdata/invalid-kfunc.c� bpf_kfunc_call_test_mem_len_pass1();� return 1;�bpf_kfunc_call_test_mem_len_pass1�char�__ARRAY_SIZE_TYPE__�__license�.ksyms�license��� �������������,���@���������������������������������������2���$��������X���(������������������������������������������������3��������������������W����������������������"������������� ��������������� ��������������������������������,�������������@�������������P������������� �.text�.rel.BTF.ext�.llvm_addrsig�__license�.reltc�call_kfunc�.strtab�.symtab�.rel.BTF�bpf_kfunc_call_test_mem_len_pass1����������������������������������������������������������������������>����������������������*������y���������������������������������������������������@��������������������������������������0���������������������@�������������������������������������,��� ���@���������������������������� ��������������������$���������������������X������� ������������������������������R����������������������h������������������������������������N��� ���@��������������������� ������� �������������������� ����������������������������`��������������������������������� ���@���������������������0������� �����������������������Lo���������������(������������������������������������F����������������������P������x�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid-kfunc.c���������������������������������������0000664�0000000�0000000�00000000427�15202436720�0023711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; // This function declaration is incorrect on purpose. extern void bpf_kfunc_call_test_mem_len_pass1(void) __ksym; __section("tc") int call_kfunc() { bpf_kfunc_call_test_mem_len_pass1(); return 1; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_btf_map_init-eb.elf���������������������������0000664�0000000�0000000�00000002140�15202436720�0026222�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������`�����@�����@��������������������������������������������������p��������������������� �������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@��������� ���F����������K������@���O���������U���������a������ ������j��������� ������� �int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�hash_map�.maps���������������������������� ����������������� �������������.text�.maps�hash_map�.llvm_addrsig�.strtab�.symtab�.rel.BTF����������������������������������������������������������������������$�������������������������!�������=���������������������������������������������������@�����������������������������������������������������������@������� ��������������������������8��������������������������`��������������������������������4��� �������@��������������������������������������������oL��������������������� ���������������������������������,��������������������������������0����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_btf_map_init-el.elf���������������������������0000664�0000000�0000000�00000002140�15202436720�0026234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������`����������@�����@������������������������������������������������p���������������������� ������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@��������� ���F����������K������@���O���������U���������a������ ������j��������� ������� ����int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�hash_map�.maps������������������������� ������������� ��������������������.text�.maps�hash_map�.llvm_addrsig�.strtab�.symtab�.rel.BTF�������������������������������������������������������������������$����������������������!������=���������������������������������������������������@�����������������������������������������������������������@������� ������������������������������8����������������������`������������������������������������4��� ���@���������������������������������������������������Lo��������������� ������������������������������������,����������������������������0�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_btf_map_init.c��������������������������������0000664�0000000�0000000�00000000511�15202436720�0025312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, uint64_t); __uint(max_entries, 1); } hash_map __section(".maps") = { /* This forces a non-zero byte into the .maps section. */ .key = (void *)1, }; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_map-eb.elf������������������������������������0000664�0000000�0000000�00000002430�15202436720�0024346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@� �MIT������������������������������������������������������������������������������ ������������������������$����������(���������.��������:����������?������ ���H������@���S������`���_���������i��������� ���v������������������������������������������������� ��������char�__ARRAY_SIZE_TYPE__�__license�def�dummy�bpf_map_def�type�key_size�value_size�max_entries�map_flags�unsigned int�uint32_t�invalid_map�license�maps��������������������������������&�������������������� ����������������������������������� ������ �.text�maps�invalid_map�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�������������������������������������������������������������������������0��������������������������������I���������������������������������������������������@����������������������������������(�������������������������@����������������������������������������������������������D���������������������������������D��������������������������\��������������������������������@��� �������@��������������h������� �����������������������oL������������������������������������������������������8������������������������� �������H������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_map-el.elf������������������������������������0000664�0000000�0000000�00000002430�15202436720�0024360�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@� ��MIT��������������������������������������������������������������������������� ������������������������$����������(���������.��������:����������?������ ���H������@���S������`���_���������i��������� ���v������������������������������������������������� �����������char�__ARRAY_SIZE_TYPE__�__license�def�dummy�bpf_map_def�type�key_size�value_size�max_entries�map_flags�unsigned int�uint32_t�invalid_map�license�maps�����������������������������&�������������������� �������������������������������� ������������ �.text�maps�invalid_map�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF����������������������������������������������������������������������0����������������������������I���������������������������������������������������@��������������������������������������(���������������������@����������������������������������������������������������D�������������������������������������D����������������������\������������������������������������@��� ���@���������������h������ ������������������������������Lo���������������������������������������������������8���������������������� ������H�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_map.c�����������������������������������������0000664�0000000�0000000�00000000555�15202436720�0023444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" char __license[] __section("license") = "MIT"; struct { struct bpf_map_def def; uint32_t dummy; } invalid_map __section("maps") = { .def = { .type = BPF_MAP_TYPE_HASH, .key_size = 4, .value_size = 2, .max_entries = 1, }, .dummy = 1, }; ���������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_map_static-eb.elf�����������������������������0000664�0000000�0000000�00000003710�15202436720�0025717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@� �������c����*������ ���������������������������������U������������������������������������������������������������������� ���� ����������������� ��� �������������������������������� ���������@���������`��������������������� �����������������������������������������������������int�xdp_prog�xdp�/ebpf/testdata/invalid_map_static.c�__section("xdp") int xdp_prog() {� uint32_t key = 0;� void *p = bpf_map_lookup_elem(&hash_map, &key);� return !!p;�bpf_map_def�type�key_size�value_size�max_entries�map_flags�unsigned int�dummy�hash_map�maps������� �������������\���p���������������������������������������6��X����������X��\ ��� ������k��`���H��������d ���X��������d�������������������������������������������������'�����������������������������������������0�����������������`��������������������������� ���������������������������������������,�������������@�������������P�������������`�������������p������������������� �dummy�.text�.rel.BTF.ext�maps�.relxdp�hash_map�xdp_prog�.llvm_addrsig�.strtab�.symtab�.rel.BTF�������������������������������������������������������������������������G�������������������������c�������`���������������������������������������������������@����������������������������������#�������������������������@�������`����������������������������� �������@������������������������ ����������������������������������������������������(��������������������������[����������������������������������������������������������W��� �������@��������������������� ��� ������������������������������������������������������������������������������ ��� �������@����������������������`��� ��������������������9oL���������������������`���������������������������������O�������������������������@�����������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_map_static-el.elf�����������������������������0000664�0000000�0000000�00000003710�15202436720�0025731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@� ��������c�������������������������������������������U����������������������������������������������������������������� ��������� ������������ ������� ��������������������������� ���������@���������`��������������������� ��������������������������������������������������������int�xdp_prog�xdp�/ebpf/testdata/invalid_map_static.c�__section("xdp") int xdp_prog() {� uint32_t key = 0;� void *p = bpf_map_lookup_elem(&hash_map, &key);� return !!p;�bpf_map_def�type�key_size�value_size�max_entries�map_flags�unsigned int�dummy�hash_map�maps���� �������������\���p���������������������������������������6����X��������X��� \�� ������k���`��H��������� d��X���������d������������������������������������������������'�����������������������������������������0�������������`��������������������������� ���������������������������������������,�������������@�������������P�������������`�������������p�������������������������� �dummy�.text�.rel.BTF.ext�maps�.relxdp�hash_map�xdp_prog�.llvm_addrsig�.strtab�.symtab�.rel.BTF����������������������������������������������������������������������G����������������������c������`���������������������������������������������������@��������������������������������������#���������������������@�������`��������������������������������� ���@���������������������������� ������������������������������������������������(������������������������������[����������������������������������������������������������W��� ���@��������������������� ������� ������������������������������������������������������������������������������ ��� ���@����������������������`������� ��������������������9���Lo���������������`������������������������������������O����������������������@�����������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/invalid_map_static.c����������������������������������0000664�0000000�0000000�00000001245�15202436720�0025010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" struct bpf_map_def dummy __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, .map_flags = 0, }; /* The static qualifier leads to clang not emitting a symbol. */ static struct bpf_map_def hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, .map_flags = 0, }; __section("xdp") int xdp_prog() { uint32_t key = 0; void *p = bpf_map_lookup_elem(&hash_map, &key); return !!p; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/iproute2_map_compat-eb.elf����������������������������0000664�0000000�0000000�00000002040�15202436720�0026031�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������ �����@�����@��������������������������������������������������n����� ���$��� ���������������� ���������@���&������`���/���������5���������8���������@���������I���������S��������� ���`������������i����������������$�bpf_elf_map�type�size_key�size_value�max_elem�flags�id�pinning�inner_id�inner_idx�unsigned int�hash_map�maps���������������������������������� �����������������$��������������.text�maps�hash_map�.llvm_addrsig�.strtab�.symtab�.rel.BTF�����������������������������������������������������������������������#��������������������������������<���������������������������������������������������@�����������������������������������������������������������@�������$��������������������������7��������������������������d������6��������������������������3��� �������@��������������������������������������������oL������������������������������������������������������+��������������������������������0��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/iproute2_map_compat-el.elf����������������������������0000664�0000000�0000000�00000002040�15202436720�0026043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������ ����������@�����@������������������������������������������������n������ ��$��� ���������������� ���������@���&������`���/���������5���������8���������@���������I���������S��������� ���`������������i����������������$����bpf_elf_map�type�size_key�size_value�max_elem�flags�id�pinning�inner_id�inner_idx�unsigned int�hash_map�maps������������������������������� �������������$���������������������.text�maps�hash_map�.llvm_addrsig�.strtab�.symtab�.rel.BTF��������������������������������������������������������������������#����������������������������<���������������������������������������������������@�����������������������������������������������������������@�������$������������������������������7����������������������d�������6�����������������������������3��� ���@���������������������������������������������������Lo���������������������������������������������������+����������������������������0���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/iproute2_map_compat.c���������������������������������0000664�0000000�0000000�00000001413�15202436720�0025124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. It is not a valid BPF program. */ #include "common.h" #define PIN_GLOBAL_NS 2 // bpf_elf_map is a custom BPF map definition used by iproute2. // It contains the id, pinning, inner_id and inner_idx fields // in addition to the ones in struct bpf_map_def which is commonly // used in the kernel and libbpf. struct bpf_elf_map { unsigned int type; unsigned int size_key; unsigned int size_value; unsigned int max_elem; unsigned int flags; unsigned int id; unsigned int pinning; unsigned int inner_id; unsigned int inner_idx; }; struct bpf_elf_map hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .size_key = sizeof(uint32_t), .size_value = sizeof(uint64_t), .max_elem = 2, .pinning = PIN_GLOBAL_NS, }; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kconfig-eb.elf����������������������������������������0000664�0000000�0000000�00000005540�15202436720�0023510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������`�����@�����@� ���������������������a�������������������������������q�������������������������������a������� ������������������������a�������������������������q�������������������������������GPL-2.0�����������l��l������ ����������������� ��� ������������������������������������������������ ������������������������.����������4������ �����N�����������X�������h������o�����w����������� ������������������������������������������������������� ���������� ���������� ������������������������������������int�kconfig�socket�/ebpf/testdata/kconfig.c�__section("socket") int kconfig() {� if (LINUX_KERNEL_VERSION == 0)� if (LINUX_HAS_SYSCALL_WRAPPER == 0)� if (CONFIG_HZ == 0)� if (CONFIG_BPF_SYSCALL == TRI_NO)� if (CONFIG_DEFAULT_HOSTNAME[0] == 0)�}�char�__ARRAY_SIZE_TYPE__�__license�LINUX_KERNEL_VERSION�_Bool�LINUX_HAS_SYSCALL_WRAPPER�CONFIG_HZ�libbpf_tristate�TRI_NO�TRI_YES�TRI_MODULE�CONFIG_BPF_SYSCALL�CONFIG_DEFAULT_HOSTNAME�.kconfig�license������� �������������������������� ���������������� ��� ����������-��8����������Q��<��� ������Q��<���0������q��H���H������q��H ���X��������T���p��������T�����������`�����������`�����������l�����������|�����������������������������������������������������-��������������������s����������������������Y����������������������O������������������������������������������������������������������5�������������������������������������0�������������X��������������������������������������4������������@������������L������������X������������d������������|�������������,�������������@�������������P�������������`�������������p������������������������������������������������������������������������������������������������� �.text�.rel.BTF.ext�.relsocket�.llvm_addrsig�kconfig�__license�.strtab�.symtab�CONFIG_HZ�LINUX_HAS_SYSCALL_WRAPPER�LINUX_KERNEL_VERSION�CONFIG_BPF_SYSCALL�.rel.BTF�CONFIG_DEFAULT_HOSTNAME����������������������������������������������������������������������?�����������������������������������������������������������������������������������@�����������������������������������������������������������@������������������������������������ �������@��������������0�������P��� ��������������������7���������������������������������������������������������������������������������� ������B����������������������������� �������@���������������������`��� �������������������� �������������������������d������������������������������������ �������@������������������������ ��������������������oL������������������������������������������������������G�������������������������X�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kconfig-el.elf����������������������������������������0000664�0000000�0000000�00000005540�15202436720�0023522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������`����������@�����@� ����������������������a�������������������������������q�������������������������������a������ �������������������������a�������������������������q�������������������������������GPL-2.0���������l��l����������� ������������ ������� ������������������������������������������� �������������������������.����������4����� ������N�����������X�������h������o�����w���������� ������������������������������������������������������� ���������� ���������� ���������������������������������������int�kconfig�socket�/ebpf/testdata/kconfig.c�__section("socket") int kconfig() {� if (LINUX_KERNEL_VERSION == 0)� if (LINUX_HAS_SYSCALL_WRAPPER == 0)� if (CONFIG_HZ == 0)� if (CONFIG_BPF_SYSCALL == TRI_NO)� if (CONFIG_DEFAULT_HOSTNAME[0] == 0)�}�char�__ARRAY_SIZE_TYPE__�__license�LINUX_KERNEL_VERSION�_Bool�LINUX_HAS_SYSCALL_WRAPPER�CONFIG_HZ�libbpf_tristate�TRI_NO�TRI_YES�TRI_MODULE�CONFIG_BPF_SYSCALL�CONFIG_DEFAULT_HOSTNAME�.kconfig�license���� �������������������������� ���������������� ��� ����������-����8��������Q���<�� ������Q���<��0������q���H��H������q��� H��X���������T��p���������T�����������`�����������`�����������l�����������|����������������������������������������������������-��������������������s����������������������Y����������������������O������������������������������������������������������������������5���������������������������������0�������������X���������������������������������������4������������@������������L������������X������������d������������|������������,�������������@�������������P�������������`�������������p�������������������������������������������������������������������������������������������������������� �.text�.rel.BTF.ext�.relsocket�.llvm_addrsig�kconfig�__license�.strtab�.symtab�CONFIG_HZ�LINUX_HAS_SYSCALL_WRAPPER�LINUX_KERNEL_VERSION�CONFIG_BPF_SYSCALL�.rel.BTF�CONFIG_DEFAULT_HOSTNAME�������������������������������������������������������������������?�������������������������������������������������������������������������������@�����������������������������������������������������������@���������������������������������������� ���@���������������0������P������� ��������������������7������������������������������������������������������������������������������� ������B�������������������������������� ���@���������������������`������� �������������������� ����������������������d��������������������������������������� ���@���������������������������� �����������������������Lo���������������������������������������������������G����������������������X�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kconfig.c���������������������������������������������0000664�0000000�0000000�00000001321�15202436720�0022571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char __license[] __section("license") = "GPL-2.0"; /* Special cases requiring feature testing or vDSO magic. */ extern int LINUX_KERNEL_VERSION __kconfig; extern _Bool LINUX_HAS_SYSCALL_WRAPPER __kconfig; /* Values pulled from /proc/kconfig. */ extern int CONFIG_HZ __kconfig; extern enum libbpf_tristate CONFIG_BPF_SYSCALL __kconfig; extern char CONFIG_DEFAULT_HOSTNAME[1] __kconfig; __section("socket") int kconfig() { if (LINUX_KERNEL_VERSION == 0) return __LINE__; if (LINUX_HAS_SYSCALL_WRAPPER == 0) return __LINE__; if (CONFIG_HZ == 0) return __LINE__; if (CONFIG_BPF_SYSCALL == TRI_NO) return __LINE__; if (CONFIG_DEFAULT_HOSTNAME[0] == 0) return __LINE__; return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kfunc-eb.elf������������������������������������������0000664�0000000�0000000�00000011060�15202436720�0023170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������0�����@�����@��*������ ��0������B������P�������������������������������������������������������������������������������������������`���������������������������`��������������������������������Dual MIT/GPL��������������������������������� ���������������������� ��� ��������� ������������������� ������� �������������� ������������������������������ �������������������� ������������������� ����������� ��������������������� ��������� ���������������/ ��������� ��������> ��������� ��������������} ��������� ������������� ��������� ���������6 ��������� �������������D ��������� ���������������������������+ ��������� ���������������> �����!��R����������������������#���%��� ��W��������� ��k������$�����u����������������������������������������������������� �����������"����������|���������&������� �ctx�int�call_kfunc�tc�/ebpf/testdata/kfunc.c�__section("tc") int call_kfunc(void *ctx) {� struct nf_conn *conn = bpf_skb_ct_lookup(ctx, (void *)buf, 0, (void *)buf, 0);� if (conn) {� bpf_ct_release(conn);� return 1;�nf_conn�__sk_buff�bpf_sock_tuple�uint32_t�unsigned int�bpf_ct_opts�bpf_skb_ct_lookup�bpf_ct_release�benchmark�fentry/bpf_fentry_test2� return bpf_fentry_test1(0);�bpf_fentry_test1�weak_kfunc_missing�tp_btf/task_newtask�__section("tp_btf/task_newtask") int weak_kfunc_missing(void *ctx) {� if (bpf_ksym_exists(invalid_kfunc)) {� invalid_kfunc();�}�invalid_kfunc�call_weak_kfunc�__section("tp_btf/task_newtask") int call_weak_kfunc(void *ctx) {� if (bpf_ksym_exists(bpf_cpumask_create)) {� struct bpf_cpumask *mask = bpf_cpumask_create();� if (mask)� bpf_cpumask_release(mask);�bpf_cpumask�bpf_cpumask_create�bpf_cpumask_release�char�__ARRAY_SIZE_TYPE__�__license�.ksyms�license����� �������<���<��L������������������������H�������������������������8����������������������.��@����������Z��H���0��������L���8��������P���H��������X��H������������`��| ��������`��|����� ����������������������������������� �����!�����0�����4�����8�����T������@����������P����������X����������h����������p���������������4����������������������������������������������������������������������������������������������������������������X�����������������������������������������������>�����������������������������������������H�����������������8��� �������������������������������8�������X���i ���������������������� ����������������������|�� ��������������� �������(������ �������@������ ������������� ���������� ���������� ��� ��� �������@��� ����������X��� ��� �������x��� ��� ������h������������t��������������������������� ������������ ������������ ����������������������,�������������<�������������L�������������T�������������h�������������x�������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x�������������������������������.text�.rel.BTF.ext�bpf_skb_ct_lookup�.reltp_btf/task_newtask�benchmark�weak_kfunc_missing�.llvm_addrsig�bpf_cpumask_create�__license�bpf_ct_release�bpf_cpumask_release�.reltc�call_kfunc�call_weak_kfunc�invalid_kfunc�.strtab�.symtab�.rel.BTF�.relfentry/bpf_fentry_test2�bpf_fentry_test1����������������������������������������������������������������������������������������������� ���������������������������������������������������������@�����������������������������������������������������������@�������X����������������������������� �������@�������������� ������� ������������������������������������������������������������������������������������ �������@�������������� ������������������������������*����������������������������������������������������������&��� �������@�������������� �������P�����������������������~������������������������@������� ���������������������������������������������������P������H����������������������������� �������@�������������� (�������p������ ����������������� ������������������������������������������������������������ �������@�������������� ������p������ �����������������[oL��������������������� ���������������������������������������������������������� @������h����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kfunc-el.elf������������������������������������������0000664�0000000�0000000�00000011060�15202436720�0023202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������0����������@�����@�����������������$�����������������������������������������������������������������������������������������������������������������������������������������������`�������������Dual MIT/GPL����������������������������������� ������������������ �� ����� ��������� �������������� ������� �������������� ������������������������������ �������������������� ������������������� ����������� �������������������������� ��������� �����������/���� ���������� ���>���� ��������� ����������}���� ��������� ������������� ���������� ����6���� ��������� ���������D���� ���������� ����������������������+���� ��������� �����������>���� !���R���������������������#���%��� ���W�������� ���k�����$������u���������������������������������������������������� �����������"�����������|��������&������� ����ctx�int�call_kfunc�tc�/ebpf/testdata/kfunc.c�__section("tc") int call_kfunc(void *ctx) {� struct nf_conn *conn = bpf_skb_ct_lookup(ctx, (void *)buf, 0, (void *)buf, 0);� if (conn) {� bpf_ct_release(conn);� return 1;�nf_conn�__sk_buff�bpf_sock_tuple�uint32_t�unsigned int�bpf_ct_opts�bpf_skb_ct_lookup�bpf_ct_release�benchmark�fentry/bpf_fentry_test2� return bpf_fentry_test1(0);�bpf_fentry_test1�weak_kfunc_missing�tp_btf/task_newtask�__section("tp_btf/task_newtask") int weak_kfunc_missing(void *ctx) {� if (bpf_ksym_exists(invalid_kfunc)) {� invalid_kfunc();�}�invalid_kfunc�call_weak_kfunc�__section("tp_btf/task_newtask") int call_weak_kfunc(void *ctx) {� if (bpf_ksym_exists(bpf_cpumask_create)) {� struct bpf_cpumask *mask = bpf_cpumask_create();� if (mask)� bpf_cpumask_release(mask);�bpf_cpumask�bpf_cpumask_create�bpf_cpumask_release�char�__ARRAY_SIZE_TYPE__�__license�.ksyms�license�� �������<���<���L������������������������H������������������������8����������������������.����@��������Z���H��0���������L��8���������P��H���������X��H������������`�� |��������`��|���� ����������������������������������� ������!����0������4����8������T�����@����������P����������X����������h����������p����������������4�����������������������������������������������������������������������������������������������������������X���������������������������������������������������>�����������������������������������������H�������������8���������� ������������������������8�������X�������i��� ���������������������� �������������������|���� ��������� �������(������� ������@������� ������������� ���������������� ��� ������� ��� ���@���������� ���X������� ��� ���x������� ��� ���h������������t��������������������������������� ������������ ������������ ���������������,�������������<�������������L�������������T�������������h�������������x��������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x�������������������������������������.text�.rel.BTF.ext�bpf_skb_ct_lookup�.reltp_btf/task_newtask�benchmark�weak_kfunc_missing�.llvm_addrsig�bpf_cpumask_create�__license�bpf_ct_release�bpf_cpumask_release�.reltc�call_kfunc�call_weak_kfunc�invalid_kfunc�.strtab�.symtab�.rel.BTF�.relfentry/bpf_fentry_test2�bpf_fentry_test1����������������������������������������������������������������������������������������� ��������������������������������������������������������@�����������������������������������������������������������@�������X��������������������������������� ���@��������������� ������ ���������������������������������������������������������������������������������������� ���@��������������� ���������������������������������*����������������������������������������������������������&��� ���@��������������� ������P���������������������������~���������������������@������ ����������������������������������������������������P������H�������������������������������� ���@���������������( ������p���������� ����������������� ������������������������������������������������������������ ���@��������������� ������p��������� �����������������[���Lo��������������� ����������������������������������������������������������@ ������h����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kfunc-kmod-eb.elf�������������������������������������0000664�0000000�0000000�00000003240�15202436720�0024121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@� ����������������������Dual MIT/GPL������������������������� ����������������� ��� ��������� ����������������Z ��������u���������������������������� ���z��������� �������������������������������������������� ������� �int�call_kfunc�tc�/ebpf/testdata/kfunc-kmod.c� bpf_testmod_test_mod_kfunc(0);� return 1;�bpf_testmod_test_mod_kfunc�char�__ARRAY_SIZE_TYPE__�__license�.ksyms�license������ �������������,���@���������������������������������������/�� ���������O��$�������������������������������������������������3����������������� ���>����������������������"����������������� ������������� ���������������������������������,�������������@�������������P������ �.text�.rel.BTF.ext�.llvm_addrsig�__license�.reltc�call_kfunc�bpf_testmod_test_mod_kfunc�.strtab�.symtab�.rel.BTF������������������������������������������������������������������������Y�������������������������*�������r���������������������������������������������������@����������������������������������0�������������������������@������� ��������������������������,��� �������@������������������������ ��������������������$�������������������������`������� ��������������������������m��������������������������p��������������������������������i��� �������@��������������������� ��� �������������������� ��������������������������������`����������������������������� �������@���������������������0��� ��������������������oL���������������������(���������������������������������a�������������������������P�������x������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kfunc-kmod-el.elf�������������������������������������0000664�0000000�0000000�00000003240�15202436720�0024133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@� �����������������������Dual MIT/GPL���������������������������� ������������ ������� ��������� �����������Z����� ���u���������������������������� ���z��������� �������������������������������������������� ������� ����int�call_kfunc�tc�/ebpf/testdata/kfunc-kmod.c� bpf_testmod_test_mod_kfunc(0);� return 1;�bpf_testmod_test_mod_kfunc�char�__ARRAY_SIZE_TYPE__�__license�.ksyms�license��� �������������,���@���������������������������������������/��� ��������O���$������������������������������������������������3������������� �������>����������������������"������������� �������������� ��������������������������������,�������������@�������������P������������� �.text�.rel.BTF.ext�.llvm_addrsig�__license�.reltc�call_kfunc�bpf_testmod_test_mod_kfunc�.strtab�.symtab�.rel.BTF���������������������������������������������������������������������Y����������������������*������r���������������������������������������������������@��������������������������������������0���������������������@������� ������������������������������,��� ���@���������������������������� ��������������������$���������������������`������� ������������������������������m����������������������p������������������������������������i��� ���@��������������������� ������� �������������������� ����������������������������`��������������������������������� ���@���������������������0������� �����������������������Lo���������������(������������������������������������a����������������������P������x�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kfunc-kmod.c������������������������������������������0000664�0000000�0000000�00000000323�15202436720�0023210�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; extern void bpf_testmod_test_mod_kfunc(int) __ksym; __section("tc") int call_kfunc() { bpf_testmod_test_mod_kfunc(0); return 1; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/kfunc.c�����������������������������������������������0000664�0000000�0000000�00000003144�15202436720�0022264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; // CO-RE type compat checking doesn't allow matches between forward declarations // and structs so we can't use forward declarations. Empty structs work just fine. struct __sk_buff {}; struct nf_conn {}; struct bpf_sock_tuple {}; struct bpf_ct_opts {}; struct bpf_cpumask {}; extern struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *, struct bpf_sock_tuple *, uint32_t, struct bpf_ct_opts *, uint32_t) __ksym; extern void bpf_ct_release(struct nf_conn *) __ksym; __section("tc") int call_kfunc(void *ctx) { char buf[1]; struct nf_conn *conn = bpf_skb_ct_lookup(ctx, (void *)buf, 0, (void *)buf, 0); if (conn) { bpf_ct_release(conn); } return 1; } extern int bpf_fentry_test1(int) __ksym; __section("fentry/bpf_fentry_test2") int benchmark() { // bpf_fentry_test1 is a valid kfunc but not allowed to be called from // TC context. We use this to avoid loading a gajillion programs into // the kernel when benchmarking the loader. return bpf_fentry_test1(0); } extern void invalid_kfunc(void) __ksym __weak; extern struct bpf_cpumask *bpf_cpumask_create(void) __ksym __weak; extern void bpf_cpumask_release(struct bpf_cpumask *cpumask) __ksym __weak; __section("tp_btf/task_newtask") int weak_kfunc_missing(void *ctx) { if (bpf_ksym_exists(invalid_kfunc)) { invalid_kfunc(); return 0; } return 1; } __section("tp_btf/task_newtask") int call_weak_kfunc(void *ctx) { if (bpf_ksym_exists(bpf_cpumask_create)) { struct bpf_cpumask *mask = bpf_cpumask_create(); if (mask) bpf_cpumask_release(mask); return 1; } return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/ksym-eb.elf�������������������������������������������0000664�0000000�0000000�00000006130�15202436720�0023047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������ �����@�����@� ���������������{����������c����j������`��z������p����������������&������7������@�����������������c������������������{������������������&������7������@�����������������������������������������������������������������Dual MIT/GPL����������������������������������������������������������������������� �������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@��������� ���F����������K������@���W���������[���������a������ ������� ���������k ����� ���� �������� ����������������������������������� ������������������������������������������������������������������������������������������������ ������� ������������������ �int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�max_entries�key�value�array_map�ksym_test�socket�/ebpf/testdata/ksym.c� val = (uint64_t)&bpf_init;� i = 0;� bpf_map_update_elem(&array_map, &i, &val, 0);� i = 1;� val = (uint64_t)&bpf_trace_run1;� return 0;�ksym_missing_test�__section("socket") int ksym_missing_test() {� if (&non_existing_symbol == 0) {�}�char�__license�bpf_init�bpf_trace_run1�non_existing_symbol�.ksyms�.maps�license������� ��������������������������u����������������������u��� �������|�����X��� ���|�����T���H���|�����\������|�����d������|�����h������|�����l������|����t������|��0���������|��^�������|�����������������������������������������������������������������������������0����������������������J����������������� ��� ��������������������������������������0���T ����������������������v����������������� ���������������������H����������������������������������������������������������������������������������������������������������������,�������������4�������������H�������������X�������������h�������������x������������������������������������������������������������������������������������  �.text�.rel.BTF.ext�ksym_test�ksym_missing_test�bpf_init�.relsocket�.maps�array_map�non_existing_symbol�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�bpf_trace_run1����������������������������������������������������������������������������������������������o����������������������������������������������������������@����������������������������������=�������������������������@��������������������������������9��� �������@���������������������P��� ��������������������x������������������������P������� ��������������������������D������������������������`������� �������������������������������������������������������������������������������������� �������@��������������X�������P��� �������������������� �������������������������D������������������������������������ �������@������������������������ ��� �����������������hoL���������������������h����������������������������������������������������������0�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/ksym-el.elf�������������������������������������������0000664�0000000�0000000�00000006130�15202436720�0023061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������ ����������@�����@� ����������������{����������c����������������������������������b������s�����������������������c������������������{������������������b������s�����������������������������������������������������������������������Dual MIT/GPL��������������������������������������������������������������������� ������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@��������� ���F����������K������@���W���������[���������a������ ������������� ���k����� ���������� ������� ������������������������������ ������������������������������������������������������������������������������������������������ ������� ������������������ ����int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�max_entries�key�value�array_map�ksym_test�socket�/ebpf/testdata/ksym.c� val = (uint64_t)&bpf_init;� i = 0;� bpf_map_update_elem(&array_map, &i, &val, 0);� i = 1;� val = (uint64_t)&bpf_trace_run1;� return 0;�ksym_missing_test�__section("socket") int ksym_missing_test() {� if (&non_existing_symbol == 0) {�}�char�__license�bpf_init�bpf_trace_run1�non_existing_symbol�.ksyms�.maps�license���� ��������������������������u����������������������u��� �������|������X�� ���|������T��H���|������\�����|������d�����|������h�����|������l�����|�����t�����|���0��������|���^������|�����������������������������������������������������������������������������0����������������������J������������� ���������� �������������������������������0�������T��� �������������������v������������� ���������������������H����������������������������������������������������������������������������������������������������������������,�������������4�������������H�������������X�������������h�������������x�������������������������������������������������������������������������������������������  �.text�.rel.BTF.ext�ksym_test�ksym_missing_test�bpf_init�.relsocket�.maps�array_map�non_existing_symbol�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�bpf_trace_run1����������������������������������������������������������������������������������������o���������������������������������������������������������@��������������������������������������=���������������������@������������������������������������9��� ���@���������������������P������� ��������������������x���������������������P������ ������������������������������D���������������������`������ ������������������������������������������������������������������������������������������ ���@���������������X������P������� �������������������� ����������������������D��������������������������������������� ���@���������������������������� ��� �����������������h���Lo���������������h����������������������������������������������������������0�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/ksym.c������������������������������������������������0000664�0000000�0000000�00000001455�15202436720�0022144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 2); __type(key, uint32_t); __type(value, uint64_t); } array_map __section(".maps"); // Non-weak ksyms must be present in the kernel. extern void bpf_init __ksym; // Weak ksyms are potentially zero at runtime. extern void bpf_trace_run1 __ksym __weak; __section("socket") int ksym_test() { uint32_t i; uint64_t val; i = 0; val = (uint64_t)&bpf_init; bpf_map_update_elem(&array_map, &i, &val, 0); i = 1; val = (uint64_t)&bpf_trace_run1; bpf_map_update_elem(&array_map, &i, &val, 0); return 0; } extern void non_existing_symbol __ksym __weak; __section("socket") int ksym_missing_test() { if (&non_existing_symbol == 0) { return 1; } return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/linked-el.elf�����������������������������������������0000664�0000000�0000000�00000007150�15202436720�0023347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������ ����������@�����@� ���.strtab�.symtab�.text�socket�maps�.maps�entry_l2�l2�l1�l1_w�entry_l1_w�l1_s�entry_l1_s�ww�entry_ww�map_legacy_l1_s�map_legacy_l2_s�map_l1_w�map_l1_s�map_ww�map_l1�.relsocket�entry_l1�map_l2�.BTF�.BTF.ext������������������������������������������������������������������������)��������������������2�����@��������������5��������������������8�����P��������������=�����P��������������H����� ��������������M����� ��������������X���"��0��������������[���"��0��������������d��������������������t�����<�������������������������� ������������ ������� ����������!��@������� ������������`������� ������������@�������������������������� ������������������������#���������������������������������������������������������������������+�������������3�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������� ������0������� ��� ���@������� ������`������� ������p������� ��� �����������P��P����������������������� ������������������������������� ����������������������"��������� ���/����� ���7����������<������@���@���������F���������R������������[������������d������������k������������������� ���r����� ���{����� ���~����� �������� �������� �������� �������� �������� �������� �����������7���������������� ���������@���F������`������������������������������������i�����������p���� �������P����������������������������� ������� ��� ��� ��� ��� ���@��� ��� ���`��� ��������� ����int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�h32_btf�type�key�value�max_entries�map_l1_w�map_l1_s�map_ww�map_l1�entry_l2�l2�l1�l1_w�entry_l1_w�l1_s�entry_l1_s�ww�entry_ww�bpf_map_def�key_size�value_size�map_flags�map_legacy_l1_s�map_legacy_l2_s�/ebpf/testdata/linked1.c� return l2();� return l1_w();� return l1_s();� return ww();� return 0;� return __LINE__;�map_l2�entry_l1�/ebpf/testdata/linked2.c� return l1();�maps�.maps�.text�socket�� �������������t����������������������������� ������0������@������P������`������p������������������������ ������0������@������P������`������p���������������������L��x��������W���� ������L����0������L����@���y��L��x��P���y��L����`���y��W����p���y��W������������������ d����������d���������� ������������ ������.����0������>�� ��8������>����@���y���� d��H���y����d��P���y������`���y��.�� ��h���y��.����p���y��>�� ��x���y��>�������������������������������������������������������������������������� ���������������@������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������p���������������������������������������������������������������P������������������������������#���������������������@��������������������������������������� ���@���������������@������`������������������������������������������������������� ��������������������������������������������������� ������(�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/linked.h����������������������������������������������0000664�0000000�0000000�00000001401�15202436720�0022423�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#pragma once #include "common.h" /* When linking BTF map definitions, all maps must be compatible with each * other, otherwise bpftool throws an error. */ struct h32_btf { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, uint32_t); __uint(max_entries, 1); }; /* Legacy map definitions are appended like programs sections are, and can * win/lose based on linking order, even if they're completely different maps. * Test whether the expected candidate wins by configuring different maxentries. */ #define h32_legacy(MAX_ENTRIES) \ { \ .type = BPF_MAP_TYPE_HASH, \ .key_size = sizeof(int), \ .value_size = sizeof(int), \ .max_entries = MAX_ENTRIES, \ .map_flags = BPF_F_NO_PREALLOC, \ } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/linked1-el.elf����������������������������������������0000664�0000000�0000000�00000007070�15202436720�0023431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������ ����������@�����@� �������������������#������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������������������������������� ����������������������"��������� ���/����� ���7����������<������@���@���������F���������R������������[������������d������������k������������������� ���r����� ���������� �������� ���������� �������� ���������� �������� ���������� �������� ���������� �������� ���������� �������� ���������� ������� ���������� ������� ���,�������7����������8����� ���A�����@���F������`���L��������V�����������f�����������v�������� ������� ��� ������� ��� ������� ��� ������� ���|�������� ����������!�����������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�h32_btf�type�key�value�max_entries�map_l1_w�map_l1_s�map_ww�map_l1�entry_l2�socket�/ebpf/testdata/linked1.c� return l2();�l2�l1�.text� return 0;�l1_w� return __LINE__;�entry_l1_w� return l1_w();�l1_s�entry_l1_s� return l1_s();�ww�entry_ww� return ww();�bpf_map_def�key_size�value_size�map_flags�map_legacy_l1_s�map_legacy_l2_s�.maps�maps����� �������T���T���������������{������������������� ������0������������������������� ������0���������{���������������� d�����������d����������� ������������� ����������0�������� ��8��������������������������x������������� �����������0��������������������������������������������������������������������������������������������������������������������������������"����������������� ���"�������������������"����������������x����� ��������������Y����� �����������������"��0�����������������"��0��������������d��������������������I���!�������������������!���������� �������t����� ������� ������� ���!��@������� ������������`������� ��������������� ������������� ������0������� ��� ���P������������\������������h������������t��������������������� ������������ ���,�������������4�������������<�������������D�������������T�������������\�������������d�������������l���������������������������������������������������������������������������������������������������������������������������������������������(������������ �entry_ww�map_ww�entry_l1_w�map_l1_w�.text�.rel.BTF.ext�.relsocket�.maps�map_legacy_l2_s�entry_l1_s�map_legacy_l1_s�map_l1_s�.llvm_addrsig�.strtab�.symtab�.rel.BTF�entry_l2�map_l1�����������������������������������������������������������������������������������������B ������������������������������������%���������������������@�������@������������������������������<����������������������������@������������������������������8��� ���@���������������x������0������� ��������������������D����������������������������(������������������������������C��������������������������������������������������������������������������������h������!�������������������������������� ���@���������������������`������� ��������������������/����������������������������8�����������������������������+��� ���@��������������� ������0������ ��� �����������������}���Lo���������������8 ������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/linked1.c���������������������������������������������0000664�0000000�0000000�00000002233�15202436720�0022503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" #include "linked.h" // Weak in L1, strong in L2. __weak __section(".maps") struct h32_btf map_l1_w; // Strong in L1, weak in L2. __section(".maps") struct h32_btf map_l1_s; // Weak in both L1 and L2. __weak __section(".maps") struct h32_btf map_ww; // Strong in L1, only defined here. __section(".maps") struct h32_btf map_l1; // Strong in L1, weak in L2. __section("maps") struct bpf_map_def map_legacy_l1_s = h32_legacy(1); // Weak in L1, strong in L2. __weak __section("maps") struct bpf_map_def map_legacy_l2_s = h32_legacy(__LINE__); // Call external symbol only defined in L2. extern int l2(void); __section("socket") int entry_l2() { return l2(); } // Weak and only defined in L1, called extern in L2. __weak __noinline int l1() { return 0; } // Weak in L1, strong in L2. __weak __noinline int l1_w() { return __LINE__; } __weak __section("socket") int entry_l1_w() { return l1_w(); } // Strong in L1, weak in L2. __noinline int l1_s() { return 0; } __section("socket") int entry_l1_s() { return l1_s(); } // Weak in both L1 and L2. __weak __noinline int ww() { return 0; } __weak __section("socket") int entry_ww() { return ww(); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/linked2-el.elf����������������������������������������0000664�0000000�0000000�00000007070�15202436720�0023432�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������ ����������@�����@� ���������������������������������+�������������3�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������������������������� ����������������������"��������� ���/����� ���7����������<������@���@���������F���������R������������[������������d������������k������������������� ���r����� ���������� �������� ���������� �������� ���������� �������� ���������� �������� ���������� �������� ���������� �������� ���������� ������� ���������� ������� ���,�������7����������8����� ���A�����@���F������`���L��������V�����������f�����������v�������� ������� ��� ������� ��� ������� ��� ������� ���|�������� ����������!�����������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�h32_btf�type�key�value�max_entries�map_l1_w�map_l1_s�map_ww�map_l2�entry_l1�socket�/ebpf/testdata/linked2.c� return l1();�l1�l2�.text� return 0;�l1_w�entry_l1_w� return l1_w();�l1_s� return __LINE__;�entry_l1_s� return l1_s();�ww�entry_ww� return ww();�bpf_map_def�key_size�value_size�map_flags�map_legacy_l1_s�map_legacy_l2_s�.maps�maps����� �������T���T���������������{������������������� ������0������������������������� ������0���������{���������������� d�����������d������������� �������� ��(����������0�������� ��8��������������������������x������������� �����������0��������������������������������������������������������������������������������������������������������������������������������"����������������� ��������������������������������������x���"�� ��������������Y���"�� �����������������"��0�����������������"��0��������������d���!�����������������I�������������������������������� �������t���!�� ������� ������� ���!��@������� ������������`������� ��������������� ������ ������� ������0������� ��� ���P������������\������������h������������t��������������������� ������������ ���,�������������4�������������<�������������D�������������T�������������\�������������d�������������l���������������������������������������������������������������������������������������������������������������������������������������������(������������ �entry_ww�map_ww�entry_l1_w�map_l1_w�.text�.rel.BTF.ext�.relsocket�.maps�map_legacy_l2_s�entry_l1_s�map_legacy_l1_s�map_l1_s�.llvm_addrsig�.strtab�.symtab�.rel.BTF�map_l2�entry_l1�����������������������������������������������������������������������������������������B ������������������������������������%���������������������@�������@������������������������������<����������������������������@������������������������������8��� ���@���������������x������0������� ��������������������D����������������������������(������������������������������C��������������������������������������������������������������������������������h������!�������������������������������� ���@���������������������`������� ��������������������/����������������������������8�����������������������������+��� ���@��������������� ������0������ ��� �����������������}���Lo���������������8 ������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/linked2.c���������������������������������������������0000664�0000000�0000000�00000002242�15202436720�0022504�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" #include "linked.h" // Weak in L1, strong in L2. __section(".maps") struct h32_btf map_l1_w; // Strong in L1, weak in L2. __weak __section(".maps") struct h32_btf map_l1_s; // Weak in both L1 and L2. __weak __section(".maps") struct h32_btf map_ww; // Strong in L2, only defined here. __section(".maps") struct h32_btf map_l2; // Strong in L1, weak in L2. __weak __section("maps") struct bpf_map_def map_legacy_l1_s = h32_legacy(__LINE__); // Weak in L1, strong in L2. __section("maps") struct bpf_map_def map_legacy_l2_s = h32_legacy(1); // Call external symbol only defined in L1. extern int l1(void); __section("socket") int entry_l1() { return l1(); } // Weak and only defined in L2, called extern in L1. __weak __noinline int l2() { return 0; } // Weak in L1, strong in L2. __noinline int l1_w() { return 0; } __section("socket") int entry_l1_w() { return l1_w(); } // Strong in L1, weak in L2. __weak __noinline int l1_s() { return __LINE__; } __weak __section("socket") int entry_l1_s() { return l1_s(); } // Weak in both L1 and L2. __weak __noinline int ww() { return __LINE__; } __weak __section("socket") int entry_ww() { return ww(); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-clang-14-eb.elf��������������������������������0000664�0000000�0000000�00000025760�15202436720�0024650�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������$p�����@�����@���������������������a��������p������������������������������������������������������������������������������������������������������ ���������������������������������� ���������������������������������� ��������������������p��������������a��������`������a����������������������������a��������������������������a�������������������������������������������������������������C��������������a������ ����������]�����������F��������������a������ ����������]�����������I�������������a������ ����������]������������������a������������� ���������������������L�������������c����a*���� ����������a*���� ����������������a*����U ����g����� ��������������!������y�������������MIT������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@���������(���F����������K������@���O���������U���������a���������k������ ������������������������������������������������������������������������������������������������������ ���F����������t��� ���@���}���������U���������������������������(���F����������K������@���O���������U��������������������������������������������������������� ������������������ ���F����������K������@���O���������U�������������������������������������������������F����������t��� ���@���U������������������������������������� ��������� ���F����������U������@���K���������O���������������������������������������F����������t��� ���@���U������������!������������"�������������%������������������������������'�������������� ���������� ���@������������F��� �������U���$���@���O���&������������(������������+��������� ���F����������t��� ���@���}���������U��������������*������������ ���F����������t��� ���@���}���������U��������������-�����#������.��%������.���� �������'�����+ ������1���� �������'����� �����3���� �������'����� �����5���� �������'����� �����7���� �������� �����9���� �������� �����;���� �������� �����=���� ��������J �����?���� �������� �����A��~����������������������C��������������D�������������F����������t������ ���}������@���U������`���a��������������F�����#������G��%������G���� ��������������J�����������J������� ������J��������M������� ������P���� ��������'������O�����������O�����������P�����������M������� ������V���� ��������������U�����������M������������U���������������K������������������L������������������S������������������ �������(���������� ����������(�������������#����������)����������,������� ���.������� �����������N����������Q����������T����������W����������X���������Y�����������������R������������������E������������������G��������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�map_flags�hash_map�key_size�value_size�hash_map2�pinning�btf_pin�inner_map_t�values�btf_outer_map�btf_outer_map_anon�perf_event�foo�bar�perf_event_array�array_map_t�btf_typedef_map�btf_decl_map�a�b�arg�static_fn�static�/ebpf/testdata/loader.c�static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) {� return arg - 1;�global_fn2�.text�int __attribute__((noinline)) global_fn2(uint32_t arg) {� return arg + 2;�global_fn3�other�int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) {� return arg + 1;�global_fn�int __attribute__((noinline)) global_fn(uint32_t arg) {� return static_fn(arg) + global_fn2(arg) + global_fn3(arg);�xdp_prog�xdp� bpf_map_lookup_elem(&hash_map, (void *)&key1);� bpf_map_lookup_elem(&hash_map2, (void *)&key2);� bpf_map_lookup_elem(&hash_map2, (void *)&key3);� return static_fn(arg) + global_fn(arg) + arg2 + arg3;�no_relocation�socket� return 0;�asm_relocation�socket/2� asm("%0 = MY_CONST ll" : "=r"(my_const));� return my_const;�data_sections�socket/3�__section("socket/3") int data_sections() {� if (uneg != (unsigned int)-1)� if (neg != -2)� if (static_uneg != (unsigned int)-3)� if (static_neg != -4)�}�anon_const�socket/4�__section("socket/4") int anon_const() {� volatile int ctx = 0;� if (ctx == values[i]) {� return values[i];�char�__license�bpf_map_def�bpf_decl_map�key1�key2�key3�arg2�arg3�uneg�neg�static_uneg�static_neg�.bss�.data�.data.test�.maps�.rodata�.rodata.test�license�maps�������� ������������t������������5����������2������������4������8������������6������������:������������<������������>��X����������@������������B�����5���������<��T��8������<����< �����<����<����� ������<����H������<����L �����<����L�����<����h���� ��<����l ���0��<����l���H��<����l���P��<����l,���`��<����l*���h��<����l�����������<��$��X������<��p��\ �����<��p��\����� ������<�� �����(��<��<�����P��<��m�����x��<���������<���� �����<���������<���������<����+�����<����)�����<����2�����<����0�����<���������������<���������������<�� �������<��8����X��� ������<��a�������<������0��<������@��<������h��<������x��<��� �����<��� �����<���,�����<�������������<���<����� ������<���\������<��8�`�����<��O�|�����<��O�|���(��<��O�|���0��<��O�|���H��<��O�|���P��<��O�|���X��<��i� �����<����������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������������� ������������������z��� �����������������"������������������.���������������������� ��������������������� �������X������������� ��������������������������������������������������������������������������������������������������������������������`�� �������������������������������������(������������������������������(������� ����������������������������������������������������������������������������������������������������������������������� �����������������h����������������������^�� �����������������)������������������5��������� ����������)�� �����������������9�������������������������������������������������H�������(���z���������p���������������������������������������������������������������� ������������������� ������� ������ �������8������ �������X������ ����������������������������������(�������������8�������������P�������������`�������������x������������������� ������������� ��������������������������������������������������!����������@���"����������x��������������������������`���������������������������������������������������������������������������&�������������'��������� ���(������������)���������$���*���������0���+���������H������������T������������`���!���������l���"���������x���������������������������������������$������������%����������,�������������<�������������D�������������T�������������d�������������t������������������������������������������ �������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h��������������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ������������ ���'(*-./ "#$%!&+, 012345�perf_event_array�.rel.text�.rel.BTF.ext�anon_const�.rodata.test�.data.test�socket�.bss�.maps�data_sections�other�.relxdp�btf_outer_map�btf_decl_map�bpf_decl_map�hash_map�btf_typedef_map�btf_outer_map_anon�no_relocation�asm_relocation�btf_pin�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�.strtab�.symtab�.rodata�.data�MY_CONST�.rel.BTF�LBB7_5�LBB8_4�.relsocket/4�key3�global_fn3�arg3�LBB8_3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1�������������������������������������������������������������������J�������������������������"s���������������������������������������������������������@�������x����������������������������� �������@���������������������0����������������������C����������������������������������������������������������l����������������������������������������������������������v����������������������������������������������������������r��� �������@��������������H������������������������������L������������������������������������������������������������������������������������������������������������������� �������@��������������������������� ��������������������������������������������������������������������������� �������@���������������������@������ ���������������������������������������������������������������������������� �������@��������������H������������� ����������������;���������������������������������������������������������Y���������������������������������������������������������S��������������������������������������������������������b��������������������������������������������������������Z���������������������������������������������������������4���������������������������������������������������������X���������������������������������������������������������A��������������������������������������������������������������������������������������� ���������������������� ��u��������������������������������������������������������q��� �������@��������������X������@����������������������� ������������������������������� ����������������������������� �������@������������������������������������������oL���������������������"X��������������������������������R������������������������������� ������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-clang-14-el.elf��������������������������������0000664�0000000�0000000�00000025760�15202436720�0024662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������p$����������@�����@������������������������������������a��������������x������a����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������aq��������������aq��������`��������������������a��������������������������a����������������������������������������������������������C�����������������a����������������]!��������F�����������������a����������������]!��������I����������������a����������������]!������������������a�����������������������!��������L����������������c����a��������������a��������������������a����U����g�������������������������y �������������MIT����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@���������(���F����������K������@���O���������U���������a���������k������ ������������������������������������������������������������������������������������������������������ ���F����������t��� ���@���}���������U���������������������������(���F����������K������@���O���������U��������������������������������������������������������� ������������������ ���F����������K������@���O���������U�������������������������������������������������F����������t��� ���@���U������������������������������������� ��������� ���F����������U������@���K���������O���������������������������������������F����������t��� ���@���U������������!������������"�������������%������������������������������'�������������� ���������� ���@������������F��� �������U���$���@���O���&������������(������������+��������� ���F����������t��� ���@���}���������U��������������*������������ ���F����������t��� ���@���}���������U��������������-������#�����.���%�����.��������� ���'�����+����� 1��������� ���'��������� 3��������� ���'��������� 5��������� ���'��������� 7���������� ������� 9���������� ������� ;���������� ������� =���������� ���J���� ?���������� ������� A���~���������������������C��������������D�������������F����������t������ ���}������@���U������`���a��������������F������#�����G���%�����G���������� ��������J�����������J������������� J��������M������������� P���������� ���'�����O�����������O�����������P�����������M������������� V���������� ��������U�����������M������������U���������������K������������������L������������������S������������������ �������(���������� ����������(�������������#����������)����������,������� ���.������� �����������N����������Q����������T����������W����������X���������Y�����������������R������������������E������������������G�����������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�map_flags�hash_map�key_size�value_size�hash_map2�pinning�btf_pin�inner_map_t�values�btf_outer_map�btf_outer_map_anon�perf_event�foo�bar�perf_event_array�array_map_t�btf_typedef_map�btf_decl_map�a�b�arg�static_fn�static�/ebpf/testdata/loader.c�static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) {� return arg - 1;�global_fn2�.text�int __attribute__((noinline)) global_fn2(uint32_t arg) {� return arg + 2;�global_fn3�other�int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) {� return arg + 1;�global_fn�int __attribute__((noinline)) global_fn(uint32_t arg) {� return static_fn(arg) + global_fn2(arg) + global_fn3(arg);�xdp_prog�xdp� bpf_map_lookup_elem(&hash_map, (void *)&key1);� bpf_map_lookup_elem(&hash_map2, (void *)&key2);� bpf_map_lookup_elem(&hash_map2, (void *)&key3);� return static_fn(arg) + global_fn(arg) + arg2 + arg3;�no_relocation�socket� return 0;�asm_relocation�socket/2� asm("%0 = MY_CONST ll" : "=r"(my_const));� return my_const;�data_sections�socket/3�__section("socket/3") int data_sections() {� if (uneg != (unsigned int)-1)� if (neg != -2)� if (static_uneg != (unsigned int)-3)� if (static_neg != -4)�}�anon_const�socket/4�__section("socket/4") int anon_const() {� volatile int ctx = 0;� if (ctx == values[i]) {� return values[i];�char�__license�bpf_map_def�bpf_decl_map�key1�key2�key3�arg2�arg3�uneg�neg�static_uneg�static_neg�.bss�.data�.data.test�.maps�.rodata�.rodata.test�license�maps����� �������������t������������5���������2������������4������8������������6������������:������������<������������>���X���������@������������B������5���������<��T���8�����<���� <�����<����<���� �������<�����H�����<���� L�����<����L�����<�����h�� ���<���� l��0���<����l��H���<����l��P���<����,l��`���<����*l��h���<����l�����������<��$���X�����<��p�� \�����<��p��\���� �������<�� ����(���<��<����P���<��m����x���<���������<���� �����<���������<���������<����+�����<����)�����<����2�����<����0�����<���������������<���������������<�� �������<��8����X�� �������<��a�������<�����0���<�����@���<�����h���<�����x���<���� ����<���� ����<����,����<�������������<����<��� �������<�����\����<��8��`����<��O��|����<��O��|�(���<��O��|�0���<��O��|�H���<��O��|�P���<��O��|�X���<��i�� ����<������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������������� �����������������z���� ����������������"������������������.����������������������� ��������������������� �X������������������� ��������������������������������������������������������������������������������������������������������������`������� ��������������������������������(������������������������������(������� ����������������������������������������������������������������������������������������������������������������������������� ����������������h���������������������^���� ����������������)������������������5���� ��������������)���� ����������������9��������������������������������������������H�������(�������z�����p���������������������������������������������������������������� ������������������� ������� ������� ������8������� ������X������� ���������������������������������(�������������8�������������P�������������`�������������x�������������������� ������������� ��������������������������������������������������������!���@����������"���x��������������������������`����������������������������������������������������������������������������������&�������������'��� ���������(������������)���$���������*���0���������+���H������������T������������`���������!���l���������"���x���������������������������������������������$������������%���,�������������<�������������D�������������T�������������d�������������t������������������������������������������������� �������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h��������������������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ������������ ���'(*-./ "#$%!&+, 012345�perf_event_array�.rel.text�.rel.BTF.ext�anon_const�.rodata.test�.data.test�socket�.bss�.maps�data_sections�other�.relxdp�btf_outer_map�btf_decl_map�bpf_decl_map�hash_map�btf_typedef_map�btf_outer_map_anon�no_relocation�asm_relocation�btf_pin�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�.strtab�.symtab�.rodata�.data�MY_CONST�.rel.BTF�LBB7_5�LBB8_4�.relsocket/4�key3�global_fn3�arg3�LBB8_3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1�����������������������������������������������������������������J���������������������s"��������������������������������������������������������@�������x��������������������������������� ���@���������������������0���������������������������C���������������������������������������������������������l����������������������������������������������������������v����������������������������������������������������������r��� ���@���������������H���������������������������������L������������������������������������������������������������������������������������������������������������������� ���@������������������������������� ��������������������������������������������������������������������������� ���@���������������������@���������� ���������������������������������������������������������������������������� ���@���������������H���������������� �����������������;��������������������������������������������������������Y���������������������������������������������������������S���������������������������������������������������������b��������������������������������������������������������Z��������������������������������������������������������4���������������������������������������������������������X���������������������������������������������������������A����������������������������������������������������������������������������������� ���������������������� �������u��������������������������������������������������������q�� ���@���������������X������@�������������������������� ���������������������������� �������������������������������� ���@�������������������������������������������������Lo���������������X"������������������������������������R��������������������������� ������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-clang-17-eb.elf��������������������������������0000664�0000000�0000000�00000025760�15202436720�0024653�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������$p�����@�����@���������������������a��������p������������������������������������������������������������������������������������������������������ ���������������������������������� ���������������������������������� ��������������������p��������������a��������`������a����������������������������a��������������������������a�������������������������������������������������������������C��������������a������ ����������]�����������F��������������a������ ����������]�����������I�������������a������ ����������]������������������a������������� ���������������������L�������������c����a*���� ����������a*���� ����������������a*����U ����g����� ��������������!������y�������������MIT������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@���������(���F����������K������@���O���������U���������a���������k������ ������������������������������������������������������������������������������������������������������ ���F����������t��� ���@���}���������U���������������������������(���F����������K������@���O���������U��������������������������������������������������������� ������������������ ���F����������K������@���O���������U�������������������������������������������������F����������t��� ���@���U������������������������������������� ��������� ���F����������U������@���K���������O���������������������������������������F����������t��� ���@���U������������!������������"�������������%������������������������������'�������������� ���������� ���@������������F��� �������U���$���@���O���&������������(������������+��������� ���F����������t��� ���@���}���������U��������������*������������ ���F����������t��� ���@���}���������U��������������-�����#������.��%������.���� �������'�����+ ������1���� �������'����� �����3���� �������'����� �����5���� �������'����� �����7���� �������� �����9���� �������� �����;���� �������� �����=���� ��������J �����?���� �������� �����A��~����������������������C��������������D�������������F����������t������ ���}������@���U������`���a��������������F�����#������G��%������G���� ��������������J�����������J������� ������J��������M������� ������P���� ��������'������O�����������O�����������P�����������M������� ������V���� ��������������U�����������M������������U���������������K������������������L������������������S������������������ �������(���������� ����������(�������������#����������)����������,������� ���.������� �����������N����������Q����������T����������W����������X���������Y�����������������R������������������E������������������G��������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�map_flags�hash_map�key_size�value_size�hash_map2�pinning�btf_pin�inner_map_t�values�btf_outer_map�btf_outer_map_anon�perf_event�foo�bar�perf_event_array�array_map_t�btf_typedef_map�btf_decl_map�a�b�arg�static_fn�static�/ebpf/testdata/loader.c�static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) {� return arg - 1;�global_fn2�.text�int __attribute__((noinline)) global_fn2(uint32_t arg) {� return arg + 2;�global_fn3�other�int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) {� return arg + 1;�global_fn�int __attribute__((noinline)) global_fn(uint32_t arg) {� return static_fn(arg) + global_fn2(arg) + global_fn3(arg);�xdp_prog�xdp� bpf_map_lookup_elem(&hash_map, (void *)&key1);� bpf_map_lookup_elem(&hash_map2, (void *)&key2);� bpf_map_lookup_elem(&hash_map2, (void *)&key3);� return static_fn(arg) + global_fn(arg) + arg2 + arg3;�no_relocation�socket� return 0;�asm_relocation�socket/2� asm("%0 = MY_CONST ll" : "=r"(my_const));� return my_const;�data_sections�socket/3�__section("socket/3") int data_sections() {� if (uneg != (unsigned int)-1)� if (neg != -2)� if (static_uneg != (unsigned int)-3)� if (static_neg != -4)�}�anon_const�socket/4�__section("socket/4") int anon_const() {� volatile int ctx = 0;� if (ctx == values[i]) {� return values[i];�char�__license�bpf_map_def�bpf_decl_map�key1�key2�key3�arg2�arg3�uneg�neg�static_uneg�static_neg�.bss�.data�.data.test�.maps�.rodata�.rodata.test�license�maps�������� ������������t������������5����������2������������4������8������������6������������:������������<������������>��X����������@������������B�����5���������<��T��8������<����< �����<����<����� ������<����H������<����L �����<����L�����<����h���� ��<����l ���0��<����l���H��<����l���P��<����l,���`��<����l*���h��<����l�����������<��$��X������<��p��\ �����<��p��\����� ������<�� �����(��<��<�����P��<��m�����x��<���������<���� �����<���������<���������<����+�����<����)�����<����2�����<����0�����<���������������<���������������<�� �������<��8����X��� ������<��a�������<������0��<������@��<������h��<������x��<��� �����<��� �����<���,�����<�������������<���<����� ������<���\������<��8�`�����<��O�|�����<��O�|���(��<��O�|���0��<��O�|���H��<��O�|���P��<��O�|���X��<��i� �����<����������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������������� ������������������z��� �����������������"������������������.���������������������� ��������������������� �������X������������� ��������������������������������������������������������������������������������������������������������������������`�� �������������������������������������(������������������������������(������� ����������������������������������������������������������������������������������������������������������������������� �����������������h����������������������^�� �����������������)������������������5��������� ����������)�� �����������������9�������������������������������������������������H�������(���z���������p���������������������������������������������������������������� ������������������� ������� ������ �������8������ �������X������ ����������������������������������(�������������8�������������P�������������`�������������x������������������� ������������� ��������������������������������������������������!����������@���"����������x��������������������������`���������������������������������������������������������������������������&�������������'��������� ���(������������)���������$���*���������0���+���������H������������T������������`���!���������l���"���������x���������������������������������������$������������%����������,�������������<�������������D�������������T�������������d�������������t������������������������������������������ �������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h��������������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ������������ ���'(*-./ "#$%!&+, 012345�perf_event_array�.rel.text�.rel.BTF.ext�anon_const�.rodata.test�.data.test�socket�.bss�.maps�data_sections�other�.relxdp�btf_outer_map�btf_decl_map�bpf_decl_map�hash_map�btf_typedef_map�btf_outer_map_anon�no_relocation�asm_relocation�btf_pin�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�.strtab�.symtab�.rodata�.data�MY_CONST�.rel.BTF�LBB7_5�LBB8_4�.relsocket/4�key3�global_fn3�arg3�LBB8_3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1�������������������������������������������������������������������J�������������������������"s���������������������������������������������������������@�������x����������������������������� �������@���������������������0����������������������C����������������������������������������������������������l����������������������������������������������������������v����������������������������������������������������������r��� �������@��������������H������������������������������L������������������������������������������������������������������������������������������������������������������� �������@��������������������������� ��������������������������������������������������������������������������� �������@���������������������@������ ���������������������������������������������������������������������������� �������@��������������H������������� ����������������;���������������������������������������������������������Y���������������������������������������������������������S��������������������������������������������������������b��������������������������������������������������������Z���������������������������������������������������������4���������������������������������������������������������X���������������������������������������������������������A��������������������������������������������������������������������������������������� ���������������������� ��u��������������������������������������������������������q��� �������@��������������X������@����������������������� ������������������������������� ����������������������������� �������@������������������������������������������oL���������������������"X��������������������������������R������������������������������� ������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-clang-17-el.elf��������������������������������0000664�0000000�0000000�00000025760�15202436720�0024665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������p$����������@�����@������������������������������������a��������������x������a����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������aq��������������aq��������`��������������������a��������������������������a����������������������������������������������������������C�����������������a����������������]!��������F�����������������a����������������]!��������I����������������a����������������]!������������������a�����������������������!��������L����������������c����a��������������a��������������������a����U����g�������������������������y �������������MIT����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@���������(���F����������K������@���O���������U���������a���������k������ ������������������������������������������������������������������������������������������������������ ���F����������t��� ���@���}���������U���������������������������(���F����������K������@���O���������U��������������������������������������������������������� ������������������ ���F����������K������@���O���������U�������������������������������������������������F����������t��� ���@���U������������������������������������� ��������� ���F����������U������@���K���������O���������������������������������������F����������t��� ���@���U������������!������������"�������������%������������������������������'�������������� ���������� ���@������������F��� �������U���$���@���O���&������������(������������+��������� ���F����������t��� ���@���}���������U��������������*������������ ���F����������t��� ���@���}���������U��������������-������#�����.���%�����.��������� ���'�����+����� 1��������� ���'��������� 3��������� ���'��������� 5��������� ���'��������� 7���������� ������� 9���������� ������� ;���������� ������� =���������� ���J���� ?���������� ������� A���~���������������������C��������������D�������������F����������t������ ���}������@���U������`���a��������������F������#�����G���%�����G���������� ��������J�����������J������������� J��������M������������� P���������� ���'�����O�����������O�����������P�����������M������������� V���������� ��������U�����������M������������U���������������K������������������L������������������S������������������ �������(���������� ����������(�������������#����������)����������,������� ���.������� �����������N����������Q����������T����������W����������X���������Y�����������������R������������������E������������������G�����������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�map_flags�hash_map�key_size�value_size�hash_map2�pinning�btf_pin�inner_map_t�values�btf_outer_map�btf_outer_map_anon�perf_event�foo�bar�perf_event_array�array_map_t�btf_typedef_map�btf_decl_map�a�b�arg�static_fn�static�/ebpf/testdata/loader.c�static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) {� return arg - 1;�global_fn2�.text�int __attribute__((noinline)) global_fn2(uint32_t arg) {� return arg + 2;�global_fn3�other�int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) {� return arg + 1;�global_fn�int __attribute__((noinline)) global_fn(uint32_t arg) {� return static_fn(arg) + global_fn2(arg) + global_fn3(arg);�xdp_prog�xdp� bpf_map_lookup_elem(&hash_map, (void *)&key1);� bpf_map_lookup_elem(&hash_map2, (void *)&key2);� bpf_map_lookup_elem(&hash_map2, (void *)&key3);� return static_fn(arg) + global_fn(arg) + arg2 + arg3;�no_relocation�socket� return 0;�asm_relocation�socket/2� asm("%0 = MY_CONST ll" : "=r"(my_const));� return my_const;�data_sections�socket/3�__section("socket/3") int data_sections() {� if (uneg != (unsigned int)-1)� if (neg != -2)� if (static_uneg != (unsigned int)-3)� if (static_neg != -4)�}�anon_const�socket/4�__section("socket/4") int anon_const() {� volatile int ctx = 0;� if (ctx == values[i]) {� return values[i];�char�__license�bpf_map_def�bpf_decl_map�key1�key2�key3�arg2�arg3�uneg�neg�static_uneg�static_neg�.bss�.data�.data.test�.maps�.rodata�.rodata.test�license�maps����� �������������t������������5���������2������������4������8������������6������������:������������<������������>���X���������@������������B������5���������<��T���8�����<���� <�����<����<���� �������<�����H�����<���� L�����<����L�����<�����h�� ���<���� l��0���<����l��H���<����l��P���<����,l��`���<����*l��h���<����l�����������<��$���X�����<��p�� \�����<��p��\���� �������<�� ����(���<��<����P���<��m����x���<���������<���� �����<���������<���������<����+�����<����)�����<����2�����<����0�����<���������������<���������������<�� �������<��8����X�� �������<��a�������<�����0���<�����@���<�����h���<�����x���<���� ����<���� ����<����,����<�������������<����<��� �������<�����\����<��8��`����<��O��|����<��O��|�(���<��O��|�0���<��O��|�H���<��O��|�P���<��O��|�X���<��i�� ����<������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������������� �����������������z���� ����������������"������������������.����������������������� ��������������������� �X������������������� ��������������������������������������������������������������������������������������������������������������`������� ��������������������������������(������������������������������(������� ����������������������������������������������������������������������������������������������������������������������������� ����������������h���������������������^���� ����������������)������������������5���� ��������������)���� ����������������9��������������������������������������������H�������(�������z�����p���������������������������������������������������������������� ������������������� ������� ������� ������8������� ������X������� ���������������������������������(�������������8�������������P�������������`�������������x�������������������� ������������� ��������������������������������������������������������!���@����������"���x��������������������������`����������������������������������������������������������������������������������&�������������'��� ���������(������������)���$���������*���0���������+���H������������T������������`���������!���l���������"���x���������������������������������������������$������������%���,�������������<�������������D�������������T�������������d�������������t������������������������������������������������� �������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h��������������������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ������������ ���'(*-./ "#$%!&+, 012345�perf_event_array�.rel.text�.rel.BTF.ext�anon_const�.rodata.test�.data.test�socket�.bss�.maps�data_sections�other�.relxdp�btf_outer_map�btf_decl_map�bpf_decl_map�hash_map�btf_typedef_map�btf_outer_map_anon�no_relocation�asm_relocation�btf_pin�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�.strtab�.symtab�.rodata�.data�MY_CONST�.rel.BTF�LBB7_5�LBB8_4�.relsocket/4�key3�global_fn3�arg3�LBB8_3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1�����������������������������������������������������������������J���������������������s"��������������������������������������������������������@�������x��������������������������������� ���@���������������������0���������������������������C���������������������������������������������������������l����������������������������������������������������������v����������������������������������������������������������r��� ���@���������������H���������������������������������L������������������������������������������������������������������������������������������������������������������� ���@������������������������������� ��������������������������������������������������������������������������� ���@���������������������@���������� ���������������������������������������������������������������������������� ���@���������������H���������������� �����������������;��������������������������������������������������������Y���������������������������������������������������������S���������������������������������������������������������b��������������������������������������������������������Z��������������������������������������������������������4���������������������������������������������������������X���������������������������������������������������������A����������������������������������������������������������������������������������� ���������������������� �������u��������������������������������������������������������q�� ���@���������������X������@�������������������������� ���������������������������� �������������������������������� ���@�������������������������������������������������Lo���������������X"������������������������������������R��������������������������� ������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-clang-20-eb.elf��������������������������������0000664�0000000�0000000�00000025570�15202436720�0024644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������#�����@�����@���������������������a��������p������������������������������������������������������������������������������������������������������ ���������������������������������� ���������������������������������� ��������������������p��������������a��������`������a����������������������������a��������������������������a�������������������������������������������������������������C��������������a������ ����������]�����������F��������������a������ ����������]�����������I�������������a������ ����������]������������������a������������� ���������������������L�������������c����a*���� ����������a*���� ����������������a*����U ����g����� ��������������!������y�������������MIT������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@���������(���F����������K������@���O���������U���������a���������k������ ������������������������������������������������������������������������������������������������������ ���F����������t��� ���@���}���������U���������������������������(���F����������K������@���O���������U��������������������������������������������������������� ������������������ ���F����������K������@���O���������U�������������������������������������������������F����������t��� ���@���U������������������������������������� ��������� ���F����������U������@���K���������O���������������������������������������F����������t��� ���@���U������������!������������"�������������%������������������������������'�������������� ���������� ���@������������F��� �������U���$���@���O���&������������(������������+��������� ���F����������t��� ���@���}���������U��������������*������������ ���F����������t��� ���@���}���������U��������������-�����#������.��%������.���� �������'�����+ ������1���� �������'����� �����3���� �������'����� �����5���� �������'����� �����7���� �������� �����9���� �������� �����;���� �������� �����=���� ��������J �����?���� �������� �����A��~����������������������C��������������D�������������F����������t������ ���}������@���U������`���a��������������F�����#������G��%������G���� ��������������J�����������J������� ������J��������M������� ������P���� ��������'������O�����������O�����������P�����������M������� ������V���� ��������������U�����������M������������U���������������K������������������L������������������S������������������ �������(���������� ����������(�������������#����������)����������,������� ���.������� �����������N����������Q����������T����������W����������X���������Y�����������������R������������������E������������������G��������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�map_flags�hash_map�key_size�value_size�hash_map2�pinning�btf_pin�inner_map_t�values�btf_outer_map�btf_outer_map_anon�perf_event�foo�bar�perf_event_array�array_map_t�btf_typedef_map�btf_decl_map�a�b�arg�static_fn�static�/ebpf/testdata/loader.c�static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) {� return arg - 1;�global_fn2�.text�int __attribute__((noinline)) global_fn2(uint32_t arg) {� return arg + 2;�global_fn3�other�int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) {� return arg + 1;�global_fn�int __attribute__((noinline)) global_fn(uint32_t arg) {� return static_fn(arg) + global_fn2(arg) + global_fn3(arg);�xdp_prog�xdp� bpf_map_lookup_elem(&hash_map, (void *)&key1);� bpf_map_lookup_elem(&hash_map2, (void *)&key2);� bpf_map_lookup_elem(&hash_map2, (void *)&key3);� return static_fn(arg) + global_fn(arg) + arg2 + arg3;�no_relocation�socket� return 0;�asm_relocation�socket/2� asm("%0 = MY_CONST ll" : "=r"(my_const));� return my_const;�data_sections�socket/3�__section("socket/3") int data_sections() {� if (uneg != (unsigned int)-1)� if (neg != -2)� if (static_uneg != (unsigned int)-3)� if (static_neg != -4)�}�anon_const�socket/4�__section("socket/4") int anon_const() {� volatile int ctx = 0;� if (ctx == values[i]) {� return values[i];�char�__license�bpf_map_def�bpf_decl_map�key1�key2�key3�arg2�arg3�uneg�neg�static_uneg�static_neg�.bss�.data�.data.test�.maps�.rodata�.rodata.test�license�maps�������� ������������d�����������5����������2������������4������8������������6������������:������������<������������>��X����������@������������B�����5���������<��T��8������<����< �����<����<����� ������<����H������<����L �����<����L�����<����h���� ��<����l ���0��<����l���H��<����l���P��<����l,���`��<����l*���h��<����l�����������<��$��X������<��p��\ �����<��p��\����� ������<�� �����(��<��<�����P��<��m�����x��<���������<���� �����<���������<���������<����+�����<����)�����<����2�����<����0�����<���������������<���������������<�� �������<��8����X��� ������<��a�������<������0��<��� ���@��<������h��<��� ���x��<��� �����<��� �����<���,�����<���<����� ������<���\������<��8�`�����<��O�|�����<��O�| ���(��<��O�|���0��<��O�| ���H��<��O�|���P��<��O�| ���X��<��i� �����<����������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������������� ������������������"������������������.���������������������� ���������������������������������������������������������������������������������������������������������������������`�� �������������������������������������(������������������������������(������� ����������������������������������������������������������������������������������������������������������������������� �����������������h����������������������^�� �����������������)������������������5��������� ����������)�� �����������������9�������������������������������������������������H�������(���z���������p���������������������������������������������������������������� ������������������� ������� ������ �������8������ �������X������ ����������������������������������(�������������8�������������P�������������`�������������x������������������� ������������� ������������������������������������������������������������@�������������x��� ������������� ����������`��� ������������������������������������������������������������������������#�������������$��������� ���%������������&���������$���'���������0���(���������H������������T������������`������������l������������x��� ������������ ������������������������!������������"����������,�������������<�������������D�������������T�������������d�������������t������������������������������������������ �������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������p��� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ���$%'*+, !"#() -./012�perf_event_array�.rel.text�.rel.BTF.ext�anon_const�.rodata.test�.data.test�socket�.bss�.maps�data_sections�other�.relxdp�btf_outer_map�btf_decl_map�bpf_decl_map�hash_map�btf_typedef_map�btf_outer_map_anon�no_relocation�asm_relocation�btf_pin�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�.strtab�.symtab�.rodata�.data�MY_CONST�.rel.BTF�.relsocket/4�key3�global_fn3�arg3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1������������������������������������������������������������������������J�������������������������" ���������������������������������������������������������@�������x����������������������������� �������@���������������������0����������������������C����������������������������������������������������������l����������������������������������������������������������v����������������������������������������������������������r��� �������@��������������������������������������������L������������������������������������������������������������������������������������������������������������������� �������@��������������������������� ��������������������������������������������������������������������������� �������@���������������������@������ ����������������~���������������������������������������������������������z��� �������@��������������������������� ����������������;���������������������������������������������������������Y���������������������������������������������������������S��������������������������������������������������������b��������������������������������������������������������Z���������������������������������������������������������4���������������������������������������������������������X���������������������������������������������������������A��������������������������������������������������������������������������������������� ���������������������� ��u��������������������������������������������������������q��� �������@���������������������@����������������������� ������������������������������������������������������������ �������@��������������@����������������������������oL���������������������!��������������������������������R�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-clang-20-el.elf��������������������������������0000664�0000000�0000000�00000025570�15202436720�0024656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������#����������@�����@������������������������������������a��������������x������a����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������aq��������������aq��������`��������������������a��������������������������a����������������������������������������������������������C�����������������a����������������]!��������F�����������������a����������������]!��������I����������������a����������������]!������������������a�����������������������!��������L����������������c����a��������������a��������������������a����U����g�������������������������y �������������MIT����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������������������������� ����������������������"��������� ���������� ���/������ ���8���������@���������(���F����������K������@���O���������U���������a���������k������ ������������������������������������������������������������������������������������������������������ ���F����������t��� ���@���}���������U���������������������������(���F����������K������@���O���������U��������������������������������������������������������� ������������������ ���F����������K������@���O���������U�������������������������������������������������F����������t��� ���@���U������������������������������������� ��������� ���F����������U������@���K���������O���������������������������������������F����������t��� ���@���U������������!������������"�������������%������������������������������'�������������� ���������� ���@������������F��� �������U���$���@���O���&������������(������������+��������� ���F����������t��� ���@���}���������U��������������*������������ ���F����������t��� ���@���}���������U��������������-������#�����.���%�����.��������� ���'�����+����� 1��������� ���'��������� 3��������� ���'��������� 5��������� ���'��������� 7���������� ������� 9���������� ������� ;���������� ������� =���������� ���J���� ?���������� ������� A���~���������������������C��������������D�������������F����������t������ ���}������@���U������`���a��������������F������#�����G���%�����G���������� ��������J�����������J������������� J��������M������������� P���������� ���'�����O�����������O�����������P�����������M������������� V���������� ��������U�����������M������������U���������������K������������������L������������������S������������������ �������(���������� ����������(�������������#����������)����������,������� ���.������� �����������N����������Q����������T����������W����������X���������Y�����������������R������������������E������������������G�����������int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�uint64_t�unsigned long�type�key�value�max_entries�map_flags�hash_map�key_size�value_size�hash_map2�pinning�btf_pin�inner_map_t�values�btf_outer_map�btf_outer_map_anon�perf_event�foo�bar�perf_event_array�array_map_t�btf_typedef_map�btf_decl_map�a�b�arg�static_fn�static�/ebpf/testdata/loader.c�static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) {� return arg - 1;�global_fn2�.text�int __attribute__((noinline)) global_fn2(uint32_t arg) {� return arg + 2;�global_fn3�other�int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) {� return arg + 1;�global_fn�int __attribute__((noinline)) global_fn(uint32_t arg) {� return static_fn(arg) + global_fn2(arg) + global_fn3(arg);�xdp_prog�xdp� bpf_map_lookup_elem(&hash_map, (void *)&key1);� bpf_map_lookup_elem(&hash_map2, (void *)&key2);� bpf_map_lookup_elem(&hash_map2, (void *)&key3);� return static_fn(arg) + global_fn(arg) + arg2 + arg3;�no_relocation�socket� return 0;�asm_relocation�socket/2� asm("%0 = MY_CONST ll" : "=r"(my_const));� return my_const;�data_sections�socket/3�__section("socket/3") int data_sections() {� if (uneg != (unsigned int)-1)� if (neg != -2)� if (static_uneg != (unsigned int)-3)� if (static_neg != -4)�}�anon_const�socket/4�__section("socket/4") int anon_const() {� volatile int ctx = 0;� if (ctx == values[i]) {� return values[i];�char�__license�bpf_map_def�bpf_decl_map�key1�key2�key3�arg2�arg3�uneg�neg�static_uneg�static_neg�.bss�.data�.data.test�.maps�.rodata�.rodata.test�license�maps����� �������������d�����������5���������2������������4������8������������6������������:������������<������������>���X���������@������������B������5���������<��T���8�����<���� <�����<����<���� �������<�����H�����<���� L�����<����L�����<�����h�� ���<���� l��0���<����l��H���<����l��P���<����,l��`���<����*l��h���<����l�����������<��$���X�����<��p�� \�����<��p��\���� �������<�� ����(���<��<����P���<��m����x���<���������<���� �����<���������<���������<����+�����<����)�����<����2�����<����0�����<���������������<���������������<�� �������<��8����X�� �������<��a�������<�����0���<���� �@���<�����h���<���� �x���<���� ����<���� ����<����,����<����<��� �������<�����\����<��8��`����<��O��|����<��O�� |�(���<��O��|�0���<��O�� |�H���<��O��|�P���<��O�� |�X���<��i�� ����<������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������������� �����������������"������������������.����������������������� ���������������������������������������������������������������������������������������������������������������`������� ��������������������������������(������������������������������(������� ����������������������������������������������������������������������������������������������������������������������������� ����������������h���������������������^���� ����������������)������������������5���� ��������������)���� ����������������9��������������������������������������������H�������(�������z�����p���������������������������������������������������������������� ������������������� ������� ������� ������8������� ������X������� ���������������������������������(�������������8�������������P�������������`�������������x�������������������� ������������� �����������������������������������������������������������@�������������x���������� ������������� ���`���������� ������������������������������������������������������������������������#�������������$��� ���������%������������&���$���������'���0���������(���H������������T������������`������������l������������x��������� ������������ ������������������������!������������"���,�������������<�������������D�������������T�������������d�������������t������������������������������������������������� �������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������p��������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ���$%'*+, !"#() -./012�perf_event_array�.rel.text�.rel.BTF.ext�anon_const�.rodata.test�.data.test�socket�.bss�.maps�data_sections�other�.relxdp�btf_outer_map�btf_decl_map�bpf_decl_map�hash_map�btf_typedef_map�btf_outer_map_anon�no_relocation�asm_relocation�btf_pin�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�.strtab�.symtab�.rodata�.data�MY_CONST�.rel.BTF�.relsocket/4�key3�global_fn3�arg3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1����������������������������������������������������������������������J��������������������� "��������������������������������������������������������@�������x��������������������������������� ���@���������������������0���������������������������C���������������������������������������������������������l����������������������������������������������������������v����������������������������������������������������������r��� ���@������������������������������������������������L������������������������������������������������������������������������������������������������������������������� ���@������������������������������� ��������������������������������������������������������������������������� ���@���������������������@���������� �����������������~���������������������������������������������������������z�� ���@������������������������������� �����������������;��������������������������������������������������������Y���������������������������������������������������������S���������������������������������������������������������b��������������������������������������������������������Z��������������������������������������������������������4���������������������������������������������������������X���������������������������������������������������������A����������������������������������������������������������������������������������� ���������������������� �������u��������������������������������������������������������q�� ���@����������������������@�������������������������� ������������������������������������������������������������ ���@���������������@����������������������������������Lo���������������!������������������������������������R���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-eb.elf�����������������������������������������0000777�0000000�0000000�00000000000�15202436720�0027150�2loader-clang-20-eb.elf������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader-el.elf�����������������������������������������0000777�0000000�0000000�00000000000�15202436720�0027174�2loader-clang-20-el.elf������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader.c����������������������������������������������0000664�0000000�0000000�00000004635�15202436720�0022432�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. */ #include "common.h" char __license[] __section("license") = "MIT"; #ifdef __NOBTF__ #include "loader_nobtf.h" #else #include "loader.h" #endif static int __attribute__((noinline)) __section("static") static_fn(uint32_t arg) { return arg - 1; } int __attribute__((noinline)) global_fn2(uint32_t arg) { return arg + 2; } int __attribute__((noinline)) __section("other") global_fn3(uint32_t arg) { return arg + 1; } int __attribute__((noinline)) global_fn(uint32_t arg) { return static_fn(arg) + global_fn2(arg) + global_fn3(arg); } volatile unsigned int key1 = 0; // .bss volatile unsigned int key2 = 1; // .data volatile const unsigned int key3 = 2; // .rodata // .rodata volatile const uint32_t arg = 1; // custom .rodata section volatile const uint32_t arg2 __section(".rodata.test") = 2; // custom .data section volatile uint32_t arg3 __section(".data.test"); __section("xdp") int xdp_prog() { bpf_map_lookup_elem(&hash_map, (void *)&key1); bpf_map_lookup_elem(&hash_map2, (void *)&key2); bpf_map_lookup_elem(&hash_map2, (void *)&key3); return static_fn(arg) + global_fn(arg) + arg2 + arg3; } // This function has no relocations, and is thus parsed differently. __section("socket") int no_relocation() { return 0; } // Make sure we allow relocations generated by inline assembly. __section("socket/2") int asm_relocation() { int my_const; asm("%0 = MY_CONST ll" : "=r"(my_const)); return my_const; } volatile const unsigned int uneg = -1; volatile const int neg = -2; static volatile const unsigned int static_uneg = -3; static volatile const int static_neg = -4; __section("socket/3") int data_sections() { if (uneg != (unsigned int)-1) return __LINE__; if (neg != -2) return __LINE__; if (static_uneg != (unsigned int)-3) return __LINE__; if (static_neg != -4) return __LINE__; return 0; } /* * Up until LLVM 14, this program results in an .rodata.cst32 section * that is accessed by 'return values[i]'. For this section, no BTF is * emitted. 'values' cannot be rewritten, since there is no BTF info * describing the data section. */ __section("socket/4") int anon_const() { volatile int ctx = 0; // 32 bytes wide results in a .rodata.cst32 section. #define values (uint64_t[]){0x0, 0x1, 0x2, 0x3} int i; for (i = 0; i < 3; i++) { if (ctx == values[i]) { return values[i]; } } return 0; } ���������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader.h����������������������������������������������0000664�0000000�0000000�00000004657�15202436720�0022443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* BTF-style map definitions for loader.c */ #pragma once #include "common.h" struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, uint64_t); __uint(max_entries, 1); __uint(map_flags, BPF_F_NO_PREALLOC); } hash_map __section(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(key_size, sizeof(uint32_t)); __uint(value_size, sizeof(uint64_t)); __uint(max_entries, 2); } hash_map2 __section(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, uint64_t); __uint(max_entries, 1); __uint(pinning, 1 /* LIBBPF_PIN_BY_NAME */); } btf_pin __section(".maps"); // Named map type definition, without structure variable declaration. struct inner_map_t { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, int); __uint(max_entries, 1); }; // Anonymous map type definition with structure variable declaration. struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __uint(key_size, sizeof(uint32_t)); __uint(max_entries, 1); __array(values, struct inner_map_t); } btf_outer_map __section(".maps"); // Array of maps with anonymous inner struct. struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __uint(key_size, sizeof(uint32_t)); __uint(max_entries, 1); __array( values, struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 1); __type(key, uint32_t); __type(value, uint32_t); }); } btf_outer_map_anon __section(".maps"); struct perf_event { uint64_t foo; uint64_t bar; }; struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(max_entries, 4096); __type(value, struct perf_event); } perf_event_array __section(".maps"); typedef struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(key_size, sizeof(uint32_t)); __uint(value_size, sizeof(uint64_t)); __uint(max_entries, 1); } array_map_t; // Map definition behind a typedef. array_map_t btf_typedef_map __section(".maps"); #define __decl_tags __attribute__((btf_decl_tag("a"), btf_decl_tag("b"))) // Legacy map definition decorated with decl tags. struct bpf_map_def bpf_decl_map __decl_tags __section("maps") = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, }; // BTF map definition decorated with decl tags. struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(key_size, sizeof(uint32_t)); __uint(value_size, sizeof(uint64_t)); __uint(max_entries, 1); } btf_decl_map __decl_tags __section(".maps"); ���������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader_nobtf-eb.elf�����������������������������������0000664�0000000�0000000�00000007750�15202436720�0024533�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������ �����@�����@���������������������a��������p������������������������������������������������������������������������������������������������������ ���������������������������������� ���������������������������������� ��������������������p��������������a��������`������a����������������������������a��������������������������a�������������������������������������������������������������C��������������a������ ����������]�����������F��������������a������ ����������]�����������I�������������a������ ����������]������������������a������������� ���������������������L�������������c����a*���� ����������a*���� ����������������a*����U ����g����� ��������������!������y�������������MIT�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������W�������������������+������������������������������������`�����������������������l��������������������������������������M������������������H�������������������&��������������������������������������b�������������������6��������������������u���������������������� ���������������������������������������P�� ���������������������������������������������� ������������ �����������������������������������������������(�������������� ������ �������8������ �������X��� ��� ����������� ������������� ����������(�������������8�������������P�������������`�������������x������������������� ���������� ��� ������������������������������������������������������������@�������������x��������������������������`������   �perf_event_array�.rel.text�anon_const�.rodata.test�.data.test�socket�.bss�maps�data_sections�other�.relxdp�hash_map�no_relocation�asm_relocation�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�loader.c�.strtab�.symtab�.rodata�.data�MY_CONST�.relsocket/4�key3�global_fn3�arg3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1����������������������������������������������������������������������������������������������������������������������������������������������������������@�������x����������������������������� �������@���������������������0���������������������������������������������������������������������������������^����������������������������������������������������������h����������������������������������������������������������d��� �������@��������������������������������������������?��������������������������������������������������������y��������������������������������������������������������u��� �������@��������������������������� ����������������?��������������������������������������������������������;��� �������@���������������������@������ ���������������������������������������������������������������������������� �������@��������������������������� ��������������������������������������������������������������������������K�������������������������������<��������������������������F�������������������������������������������������������� �����������������������������������������������������������������������������������������������������������������'���������������������������������������������������������4��������������������������������������������������������g������������������������������� ���������������������� ���oL��������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader_nobtf-el.elf�����������������������������������0000664�0000000�0000000�00000007750�15202436720�0024545�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������ ����������@�����@������������������������������������a��������������x������a����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������aq��������������aq��������`��������������������a��������������������������a����������������������������������������������������������C�����������������a����������������]!��������F�����������������a����������������]!��������I����������������a����������������]!������������������a�����������������������!��������L����������������c����a��������������a��������������������a����U����g�������������������������y �������������MIT���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������W�������������������+�������������������������������`���������������������������l���������������������������������������M������������������H�������������������&��������������������������������������b�������������������6�������������������u������������������������ �������������������������������������P���� ���������������������������������������� ������������������ �����������������������������������������(�������������� ������� ������8������� ������X������� ��� �������������� ������������� ���(�������������8�������������P�������������`�������������x�������������������� ������������� ��� ��������������������������������������������������������@�������������x��������������������������`�������������   �perf_event_array�.rel.text�anon_const�.rodata.test�.data.test�socket�.bss�maps�data_sections�other�.relxdp�hash_map�no_relocation�asm_relocation�global_fn�static_fn�arg�xdp_prog�.llvm_addrsig�static_uneg�static_neg�__license�static�loader.c�.strtab�.symtab�.rodata�.data�MY_CONST�.relsocket/4�key3�global_fn3�arg3�.relsocket/3�key2�hash_map2�global_fn2�arg2�.rodata.cst32�.relsocket/2�key1���������������������������������������������������������������������������������������������������������������������������������������������������@�������x��������������������������������� ���@���������������������0�������������������������������������������������������������������������������������^����������������������������������������������������������h����������������������������������������������������������d��� ���@������������������������������������������������?���������������������������������������������������������y��������������������������������������������������������u�� ���@������������������������������� �����������������?��������������������������������������������������������;�� ���@���������������������@���������� ���������������������������������������������������������������������������� ���@������������������������������� ��������������������������������������������������������������������������K���������������������������<������������������������������F��������������������������������������������������������� ����������������������������������������������������������������������������������������������������������������'���������������������������������������������������������4���������������������������������������������������������g�������������������������� ���������������������� ����������Lo��������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/loader_nobtf.h����������������������������������������0000664�0000000�0000000�00000001622�15202436720�0023620�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Legacy map definitions for loader.c (no BTF) */ #pragma once #include "common.h" struct bpf_map_def hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, .map_flags = BPF_F_NO_PREALLOC, }; struct bpf_map_def hash_map2 __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 2, }; // key_size and value_size always need to be 4 bytes and are automatically set // when the map is created if left at 0 in the ELF. Leave them at 0 for // consistency with the BTF map definitions, which specify key and value types, // causing sizes to be 0 in the MapSpec. This avoids special casing in tests. struct bpf_map_def perf_event_array __section("maps") = { .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, .max_entries = 4096, }; ��������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/manyprogs-eb.elf��������������������������������������0000664�0000000�0000000�00000105760�15202436720�0024114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������z0�����@�����@�G�p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������p�����{����������#������������j������`�������� �����0������������q������y:����1������������ �����������q������y:����1������j������`�������� �����������q��������������&������������U�� ����*������ ��:������0����������������@��������������������������������������Dual MIT/GPL������������������������������8��8������ ����������������� ��� ��������������������������������������������������������������&�������4��� ������7�������A��� ������F��������� ���� �������� ����� ���� ��������2 ����� ���� ��������f ��������� �������� ��������� �������� ��������� �������� ��������� ��������6 ��������� ��������j ��������� �������� ��������� �������� ��������� �������� ��������� ��������@ �����!���� ��������w �����#���� �������� �����%���� �������� �����'���� �������� �����)���� ��������S �����+���� �������� �����-���� �������� �����/���� �������� �����1���� ��������/ �����3���� ��������f �����5���� �������� �����7���� �������� �����9���� �������� �����;���� ��������B �����=���� ��������y �����?���� �������� �����A���� �������� �����C������������������������E���G��� ��#��������� ��7������F�����A�������M��� ������R��� ��� ��[��� ���@��f��� ���`��r��� �����|������I��������������H������� �����������J��������int�kprobe_execve0�kprobe/sys_execvea0�/ebpf/testdata/manyprogs.c�DEFINE_PROBE(0);� uint64_t initval = 1, *valp;� struct task_struct *task = (struct task_struct *)bpf_get_current_task();�task_struct�nsproxy�0:0� uint32_t mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);�mnt_ns�mnt_namespace�ns�ns_common�inum�unsigned int�0:0:0� valp = bpf_map_lookup_elem(&kprobe_map, &mntns);� if (!valp) {� bpf_map_update_elem(&kprobe_map, &mntns, &initval, 0);� return 0;� __sync_fetch_and_add(valp, 1);�kprobe_execve1�kprobe/sys_execvea1�DEFINE_PROBE(1);�kprobe_execve2�kprobe/sys_execvea2�DEFINE_PROBE(2);�kprobe_execve3�kprobe/sys_execvea3�DEFINE_PROBE(3);�kprobe_execve4�kprobe/sys_execvea4�DEFINE_PROBE(4);�kprobe_execve5�kprobe/sys_execvea5�DEFINE_PROBE(5);�kprobe_execve6�kprobe/sys_execvea6�DEFINE_PROBE(6);�kprobe_execve7�kprobe/sys_execvea7�DEFINE_PROBE(7);�kprobe_execve8�kprobe/sys_execvea8�DEFINE_PROBE(8);�kprobe_execve9�kprobe/sys_execvea9�DEFINE_PROBE(9);�kprobe_execve10�kprobe/sys_execvea10�DEFINE_PROBE(10);�kprobe_execve11�kprobe/sys_execvea11�DEFINE_PROBE(11);�kprobe_execve12�kprobe/sys_execvea12�DEFINE_PROBE(12);�kprobe_execve13�kprobe/sys_execvea13�DEFINE_PROBE(13);�kprobe_execve14�kprobe/sys_execvea14�DEFINE_PROBE(14);�kprobe_execve15�kprobe/sys_execvea15�DEFINE_PROBE(15);�kprobe_execve16�kprobe/sys_execvea16�DEFINE_PROBE(16);�kprobe_execve17�kprobe/sys_execvea17�DEFINE_PROBE(17);�kprobe_execve18�kprobe/sys_execvea18�DEFINE_PROBE(18);�kprobe_execve19�kprobe/sys_execvea19�DEFINE_PROBE(19);�kprobe_execve20�kprobe/sys_execvea20�DEFINE_PROBE(20);�kprobe_execve21�kprobe/sys_execvea21�DEFINE_PROBE(21);�kprobe_execve22�kprobe/sys_execvea22�DEFINE_PROBE(22);�kprobe_execve23�kprobe/sys_execvea23�DEFINE_PROBE(23);�kprobe_execve24�kprobe/sys_execvea24�DEFINE_PROBE(24);�kprobe_execve25�kprobe/sys_execvea25�DEFINE_PROBE(25);�kprobe_execve26�kprobe/sys_execvea26�DEFINE_PROBE(26);�kprobe_execve27�kprobe/sys_execvea27�DEFINE_PROBE(27);�kprobe_execve28�kprobe/sys_execvea28�DEFINE_PROBE(28);�kprobe_execve29�kprobe/sys_execvea29�DEFINE_PROBE(29);�char�__ARRAY_SIZE_TYPE__�__license�bpf_map_def�type�key_size�value_size�max_entries�map_flags�kprobe_map�license�maps����� �������������������������������� ���������� ��A������������u������������������������������������������������E������������y���������������������������������������������� ��P����������"������������$������������&������������(��,����������*��c����������,������������.������������0������������2��?����������4��v����������6������������8������������:������������<��R����������>������������@������������B������������D��������� �������(���C���������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(���C���� ��� �������(��!���������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��!����A��� �������(��U���������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��U����u��� �������(�����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��������� �������(�����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��������� �������(�����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��������� �������(��%���������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��%����E��� �������(��Y���������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��Y����y��� �������(�����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��������� �������(�����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��������� �������(�����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��������� �������(��.���������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��.����P��� �������(��e��������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��e������ �������(����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(�������� �������(��� �������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��� ����� �������(�� ��������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(�� ���,��� �������(��A��������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��A���c��� �������(��x��������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��x������ �������(����������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(�������� �������(��� �������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��� ����� �������(���(�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(���(��?��� �������(��T�,�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��T�,��v��� �������(���0�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(���0����� �������(���4�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(���4����� �������(���8�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(���8����� �������(��0�<�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��0�<��R��� �������(��g�@�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(��g�@����� �������(���D�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(���D����� �������(���H�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(���H����� �������(�� �L�������(���T�� ������(���r��3���8���(�����������(��Y�� ������(���������(������0���(������8���(������@���(�� �L�������������������������X������������������S������ �������������������X������������������S������A�������������������X������������������S������u�������������������X������������������S�������������������������X������������������S�������������������������X������������������S�������������������������X������������������S������E�������������������X������������������S������y�������������������X������������������S�������������������������X������������������S�������������������������X������������������S�������������������������X������������������S������P�������������������X������������������S�������������������������X������������������S�������������������������X������������������S�������������������������X������������������S������,�������������������X������������������S������c�������������������X������������������S�������������������������X������������������S�������������������������X������������������S�������������������������X������������������S������?�������������������X������������������S������v�������������������X������������������S�������������������������X������������������S�������������������������X������������������S�������������������������X������������������S������R�������������������X������������������S�������������������������X������������������S�������������������������X������������������S�������������������������X������������������S�������������������������������������������������������������������������������������������������������� ���������������������� ���������������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!����������������������#����������������������%����������������������'����������������������)����������������������+����������������������-����������������������/����������������������1����������������������3����������������������5����������������������7����������������������9����������������������;����������������������=����������������������������������P�����@���������������������������������P������������������P��+�� ��������������P���� ��������������P��9�� ��������������P������������������P��G����������������P�������������������P���U����������������P������������������P��m����������������P������������������P��{����������������P������������������P����!��������������P����#��������������P����%��������������P����'��������������P�����)��������������P����+��������������P��D��-��������������P����/��������������P��R��1��������������P����3��������������P��`��5��������������P����7��������������P��n��9��������������P�����;��������������P���|��=��������������P���2��?��������������� ���������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ���������0���>���������H��� ����������,�������������<�������������L�������������\�������������l�������������|������������������������������������������ ������������� ������������� ������������� ������������� ���������������������� ������������������������,������������<������������L������������\������������l������������|������������������������������������������������������������������������������������������������������������������������ ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������X������������h������������x������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������P��� ���������`��� ���������p��� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ���������(��� ���������8��� ���������H��� ���������X��� ���������h��� ���������x��� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ��������� ���� ��������� ��� ��������� ��� ��������� 0��� ��������� H��� ��������� X��� ��������� h��� ��������� x��� ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ���� ��������� ��� ��������� ��� ��������� 0��� ��������� @��� ��������� P��� ��������� `��� ��������� p��� ��������� ��� ��������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ (������������ @������������ P������������ `������������ p������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ (������������ 8������������ H������������ X������������ h������������ x������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ������������ ������������ 8������������ H������������ X������������ h������������ x������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h�������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������x������������������������������������������������������������������������������������������������������������������������ ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������p�������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������ ������������������������4������������D������������T������������l������������|������������������������������������������������������������������������������������������������������������$������������4������������L������������\������������l��������������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ���������,��� ���������<��� ���������L��� ���������d��� ���������t��� ������������ ��������������������������������������������������������������������������������� ������������������������,������������D������������T������������d������������|������������������������������������������������������������������������������������������������ ������������$������������4������������D������������\������������l������������|������������������������������������������������������������������������������������������������������������$������������<������������L������������\������������t������������������������������������������������������������������������������������������������������������������������,������������<������)+,-./0123456789:;<=>?@ABCDEFGH*�.text�.rel.BTF.ext�maps�kprobe_map�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�kprobe_execve9�.relkprobe/sys_execvea9�kprobe_execve29�.relkprobe/sys_execvea29�kprobe_execve19�.relkprobe/sys_execvea19�kprobe_execve8�.relkprobe/sys_execvea8�kprobe_execve28�.relkprobe/sys_execvea28�kprobe_execve18�.relkprobe/sys_execvea18�kprobe_execve7�.relkprobe/sys_execvea7�kprobe_execve27�.relkprobe/sys_execvea27�kprobe_execve17�.relkprobe/sys_execvea17�kprobe_execve6�.relkprobe/sys_execvea6�kprobe_execve26�.relkprobe/sys_execvea26�kprobe_execve16�.relkprobe/sys_execvea16�kprobe_execve5�.relkprobe/sys_execvea5�kprobe_execve25�.relkprobe/sys_execvea25�kprobe_execve15�.relkprobe/sys_execvea15�kprobe_execve4�.relkprobe/sys_execvea4�kprobe_execve24�.relkprobe/sys_execvea24�kprobe_execve14�.relkprobe/sys_execvea14�kprobe_execve3�.relkprobe/sys_execvea3�kprobe_execve23�.relkprobe/sys_execvea23�kprobe_execve13�.relkprobe/sys_execvea13�kprobe_execve2�.relkprobe/sys_execvea2�kprobe_execve22�.relkprobe/sys_execvea22�kprobe_execve12�.relkprobe/sys_execvea12�kprobe_execve1�.relkprobe/sys_execvea1�kprobe_execve21�.relkprobe/sys_execvea21�kprobe_execve11�.relkprobe/sys_execvea11�kprobe_execve0�.relkprobe/sys_execvea0�kprobe_execve20�.relkprobe/sys_execvea20�kprobe_execve10�.relkprobe/sys_execvea10���������������������������������������������������������������������<�������������������������u ���������������������������������������������������������@����������������������������������������������������������@������P���������������������������� �������@��������������V������� ���F�������������������0������������������������������P�������������������������,��� �������@��������������W�������� ���F�������������������������������������������������P���������������������������� �������@��������������W ������� ���F�������������������>������������������������0������P�������������������������:��� �������@��������������W@������� ���F��� ����������������������������������������������P���������������������������� �������@��������������W`������� ���F��� ����������������L������������������������������P�������������������������H��� �������@��������������W������� ���F��� ���������������������������������������� ������P���������������������������� �������@��������������W������� ���F�������������������Z������������������������ p������P�������������������������V��� �������@��������������W������� ���F�������������������������������������������� ������P����������������������������� �������@��������������W������� ���F��������������������h������������������������ ������P��������������������������d��� �������@��������������X�������� ���F������������������������������������������� `������P���������������������������� �������@��������������X ������� ���F�������������������������������������������������P�������������������������}��� �������@��������������X@������� ���F��������������������������������������������������P���������������������������� �������@��������������X`������� ���F�������������������������������������������P������P���������������������������� �������@��������������X������� ���F�������������������������������������������������P���������������������������� �������@��������������X������� ���F�������������������������������������������������P���������������������������� �������@��������������X������� ���F���!����������������$������������������������@������P������������������������� ��� �������@��������������X������� ���F���#����������������������������������������������P���������������������������� �������@��������������Y�������� ���F���%����������������2������������������������������P�������������������������.��� �������@��������������Y ������� ���F���'�����������������������������������������0������P����������������������������� �������@��������������Y@������� ���F���)����������������������������������������������P���������������������������� �������@��������������Y`������� ���F���+����������������X������������������������������P�������������������������T��� �������@��������������Y������� ���F���-���������������������������������������� ������P���������������������������� �������@��������������Y������� ���F���/����������������f������������������������p������P�������������������������b��� �������@��������������Y������� ���F���1����������������������������������������������P���������������������������� �������@��������������Y������� ���F���3����������������t������������������������!������P�������������������������p��� �������@��������������Z�������� ���F���5����������������������������������������"`������P���������������������������� �������@��������������Z ������� ���F���7����������������������������������������#������P�������������������������~��� �������@��������������Z@������� ���F���9���������������� ������������������������%�������P���������������������������� �������@��������������Z`������� ���F���;�����������������������������������������&P������P����������������������������� �������@��������������Z������� ���F���=�����������������4������������������������'������� ��������������������������������������������������'���������������������������������P�������������������������'������ ��������������������������L��� �������@��������������Z������� ���F���A����������������� �������������������������4������L����������������������������� �������@��������������Z������@���F���C�����������������$oL���������������������u�������� ��������������������������D�������������������������P������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/manyprogs-el.elf��������������������������������������0000664�0000000�0000000�00000105760�15202436720�0024126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������0z����������@�����@�G�������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p�������������������������{z�������#�����������������������a��������������������q���������y����������a��������������q���������y������������������a��������������q�����������������b������������U� �����������������������������������������������������p��������������������Dual MIT/GPL����������������������������8��8����������� ������������ ������� ����������������������������������������������������������&�������4�� �������7�������A�� �������F�������� ���������� ������� ���������� ���2���� ���������� ���f���� ���������� ������� ���������� ������� ���������� ������� ���������� ���6���� ���������� ���j���� ���������� ������� ���������� ������� ���������� ��� ���� ���������� ���@���� !���������� ���w���� #���������� ������� %���������� ������� '���������� ������� )���������� ���S���� +���������� ������� -���������� ������� /���������� ������� 1���������� ���/���� 3���������� ���f���� 5���������� ������� 7���������� ������� 9���������� ��� ���� ;���������� ���B���� =���������� ���y���� ?���������� ������� A���������� ������� C������������������������E���G��� ���#�������� ���7�����F������A�������M�� �������R�� ��� ���[�� ���@���f�� ���`���r�� ������|�����I��������������H������� �����������J�����������int�kprobe_execve0�kprobe/sys_execvea0�/ebpf/testdata/manyprogs.c�DEFINE_PROBE(0);� uint64_t initval = 1, *valp;� struct task_struct *task = (struct task_struct *)bpf_get_current_task();�task_struct�nsproxy�0:0� uint32_t mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);�mnt_ns�mnt_namespace�ns�ns_common�inum�unsigned int�0:0:0� valp = bpf_map_lookup_elem(&kprobe_map, &mntns);� if (!valp) {� bpf_map_update_elem(&kprobe_map, &mntns, &initval, 0);� return 0;� __sync_fetch_and_add(valp, 1);�kprobe_execve1�kprobe/sys_execvea1�DEFINE_PROBE(1);�kprobe_execve2�kprobe/sys_execvea2�DEFINE_PROBE(2);�kprobe_execve3�kprobe/sys_execvea3�DEFINE_PROBE(3);�kprobe_execve4�kprobe/sys_execvea4�DEFINE_PROBE(4);�kprobe_execve5�kprobe/sys_execvea5�DEFINE_PROBE(5);�kprobe_execve6�kprobe/sys_execvea6�DEFINE_PROBE(6);�kprobe_execve7�kprobe/sys_execvea7�DEFINE_PROBE(7);�kprobe_execve8�kprobe/sys_execvea8�DEFINE_PROBE(8);�kprobe_execve9�kprobe/sys_execvea9�DEFINE_PROBE(9);�kprobe_execve10�kprobe/sys_execvea10�DEFINE_PROBE(10);�kprobe_execve11�kprobe/sys_execvea11�DEFINE_PROBE(11);�kprobe_execve12�kprobe/sys_execvea12�DEFINE_PROBE(12);�kprobe_execve13�kprobe/sys_execvea13�DEFINE_PROBE(13);�kprobe_execve14�kprobe/sys_execvea14�DEFINE_PROBE(14);�kprobe_execve15�kprobe/sys_execvea15�DEFINE_PROBE(15);�kprobe_execve16�kprobe/sys_execvea16�DEFINE_PROBE(16);�kprobe_execve17�kprobe/sys_execvea17�DEFINE_PROBE(17);�kprobe_execve18�kprobe/sys_execvea18�DEFINE_PROBE(18);�kprobe_execve19�kprobe/sys_execvea19�DEFINE_PROBE(19);�kprobe_execve20�kprobe/sys_execvea20�DEFINE_PROBE(20);�kprobe_execve21�kprobe/sys_execvea21�DEFINE_PROBE(21);�kprobe_execve22�kprobe/sys_execvea22�DEFINE_PROBE(22);�kprobe_execve23�kprobe/sys_execvea23�DEFINE_PROBE(23);�kprobe_execve24�kprobe/sys_execvea24�DEFINE_PROBE(24);�kprobe_execve25�kprobe/sys_execvea25�DEFINE_PROBE(25);�kprobe_execve26�kprobe/sys_execvea26�DEFINE_PROBE(26);�kprobe_execve27�kprobe/sys_execvea27�DEFINE_PROBE(27);�kprobe_execve28�kprobe/sys_execvea28�DEFINE_PROBE(28);�kprobe_execve29�kprobe/sys_execvea29�DEFINE_PROBE(29);�char�__ARRAY_SIZE_TYPE__�__license�bpf_map_def�type�key_size�value_size�max_entries�map_flags�kprobe_map�license�maps�� ��������������������������������� ��������� ���A������������u������������������������������������������������E������������y��������������������������������������������� ���P���������"������������$������������&������������(���,���������*���c���������,������������.������������0������������2���?���������4���v���������6������������8������������:������������<���R���������>������������@������������B������������D��������� �������(���C���������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���C����� �� �������(���!��������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���!����A�� �������(���U��������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���U����u�� �������(�����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��������� �������(�����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��������� �������(�����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��������� �������(���%��������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���%����E�� �������(���Y��������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���Y����y�� �������(�����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��������� �������(�����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��������� �������(�����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��������� �������(���.��������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���.����P�� �������(���e�������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���e����� �������(����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�������� �������(������ ����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(����� ��� �������(��� �������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��� ���,�� �������(���A�������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���A���c�� �������(���x�������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���x����� �������(����������(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�������� �������(������ ����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(����� ��� �������(������(����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�����(�?�� �������(���T���,����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���T��,�v�� �������(������0����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�����0��� �������(������4����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�����4��� �������(������8����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�����8��� �������(���0���<����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���0��<�R�� �������(���g���@����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(���g��@��� �������(������D����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�����D��� �������(������H����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(�����H��� �������(��� ���L����(���T��� �����(���r���3��8���(�����������(���Y�� �����(���������(�������0��(�������8��(�������@��(��� ��L�����������������������X�������������������S������ ������������������X�������������������S������A������������������X�������������������S������u������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������E������������������X�������������������S������y������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������P������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������,������������������X�������������������S������c������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������?������������������X�������������������S������v������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������R������������������X�������������������S������������������������X�������������������S������������������������X�������������������S������������������������X�������������������S��������������������������������������������������������������������������������������������������������� ���������������������� ���������������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!����������������������#����������������������%����������������������'����������������������)����������������������+����������������������-����������������������/����������������������1����������������������3����������������������5����������������������7����������������������9����������������������;����������������������=�����������������������������P����������@����������������������������P������������������P������+��� ���������P��������� ���������P������9��� ���������P������������������P������G������������P�������������������P������U�������������P������������������P������m������������P������������������P������{������������P������������������P���������!���������P���������#���������P���������%���������P���������'���������P����������)���������P���������+���������P������D���-���������P���������/���������P������R���1���������P���������3���������P������`���5���������P���������7���������P������n���9���������P����������;���������P������|����=���������P������2����?��������� ����������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ������������� ������������ ���0���������>���H��������� ���,�������������<�������������L�������������\�������������l�������������|������������������������������������������������� ������������� ������������� ������������� ������������� ���������������� ������������������������,������������<������������L������������\������������l������������|������������������������������������������������������������������������������������������������������������������������ ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������X������������h������������x������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������P��������� ���`��������� ���p��������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ���(��������� ���8��������� ���H��������� ���X��������� ���h��������� ���x��������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ���� ��������� ��� ��������� ��� ��������� ���0 ��������� ���H ��������� ���X ��������� ���h ��������� ���x ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ��� ��������� ���� ��������� ��� ��������� ��� ��������� ���0 ��������� ���@ ��������� ���P ��������� ���` ��������� ���p ��������� ��� ��������� ��� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������( ������������@ ������������P ������������` ������������p ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������( ������������8 ������������H ������������X ������������h ������������x ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������� ������������ ������������ ������������8 ������������H ������������X ������������h ������������x ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������������������������������� ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h�������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������������������������������� ������������0������������@������������P������������`������������x������������������������������������������������������������������������������������������������������������������������ ������������0������������@������������P������������`������������p������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������p�������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������ ������������������������4������������D������������T������������l������������|������������������������������������������������������������������������������������������������������������$������������4������������L������������\������������l��������������������� ������������ ������������ ������������ ������������ ������������ ������������ ������������ ������������ ���,��������� ���<��������� ���L��������� ���d��������� ���t��������� ������������ ��������������������������������������������������������������������������� ������������������������,������������D������������T������������d������������|������������������������������������������������������������������������������������������������ ������������$������������4������������D������������\������������l������������|������������������������������������������������������������������������������������������������������������$������������<������������L������������\������������t������������������������������������������������������������������������������������������������������������������������,������������<������������)+,-./0123456789:;<=>?@ABCDEFGH*�.text�.rel.BTF.ext�maps�kprobe_map�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�kprobe_execve9�.relkprobe/sys_execvea9�kprobe_execve29�.relkprobe/sys_execvea29�kprobe_execve19�.relkprobe/sys_execvea19�kprobe_execve8�.relkprobe/sys_execvea8�kprobe_execve28�.relkprobe/sys_execvea28�kprobe_execve18�.relkprobe/sys_execvea18�kprobe_execve7�.relkprobe/sys_execvea7�kprobe_execve27�.relkprobe/sys_execvea27�kprobe_execve17�.relkprobe/sys_execvea17�kprobe_execve6�.relkprobe/sys_execvea6�kprobe_execve26�.relkprobe/sys_execvea26�kprobe_execve16�.relkprobe/sys_execvea16�kprobe_execve5�.relkprobe/sys_execvea5�kprobe_execve25�.relkprobe/sys_execvea25�kprobe_execve15�.relkprobe/sys_execvea15�kprobe_execve4�.relkprobe/sys_execvea4�kprobe_execve24�.relkprobe/sys_execvea24�kprobe_execve14�.relkprobe/sys_execvea14�kprobe_execve3�.relkprobe/sys_execvea3�kprobe_execve23�.relkprobe/sys_execvea23�kprobe_execve13�.relkprobe/sys_execvea13�kprobe_execve2�.relkprobe/sys_execvea2�kprobe_execve22�.relkprobe/sys_execvea22�kprobe_execve12�.relkprobe/sys_execvea12�kprobe_execve1�.relkprobe/sys_execvea1�kprobe_execve21�.relkprobe/sys_execvea21�kprobe_execve11�.relkprobe/sys_execvea11�kprobe_execve0�.relkprobe/sys_execvea0�kprobe_execve20�.relkprobe/sys_execvea20�kprobe_execve10�.relkprobe/sys_execvea10������������������������������������������������������������������<���������������������� u��������������������������������������������������������@����������������������������������������������������������@�������P������������������������������� ���@���������������V������ �������F��������������������0��������������������������P�����������������������������,�� ���@����������������W������ �������F����������������������������������������������P������������������������������� ���@��������������� W������ �������F��������������������>��������������������0������P�����������������������������:�� ���@���������������@W������ �������F��� �������������������������������������������P������������������������������� ���@���������������`W������ �������F��� �����������������L��������������������������P�����������������������������H�� ���@���������������W������ �������F��� ������������������������������������� ������P������������������������������� ���@���������������W������ �������F��������������������Z��������������������p ������P�����������������������������V�� ���@���������������W������ �������F����������������������������������������� ������P�������������������������������� ���@���������������W������ �������F��������������������h��������������������� ������P�����������������������������d��� ���@����������������X������ �������F����������������������������������������` ������P������������������������������� ���@��������������� X������ �������F����������������������������������������������P�����������������������������}�� ���@���������������@X������ �������F�����������������������������������������������P������������������������������� ���@���������������`X������ �������F����������������������������������������P������P������������������������������� ���@���������������X������ �������F����������������������������������������������P������������������������������� ���@���������������X������ �������F����������������������������������������������P������������������������������� ���@���������������X������ �������F���!�����������������$��������������������@������P����������������������������� �� ���@���������������X������ �������F���#�������������������������������������������P������������������������������� ���@����������������Y������ �������F���%�����������������2��������������������������P�����������������������������.�� ���@��������������� Y������ �������F���'��������������������������������������0������P�������������������������������� ���@���������������@Y������ �������F���)�������������������������������������������P������������������������������� ���@���������������`Y������ �������F���+�����������������X��������������������������P�����������������������������T�� ���@���������������Y������ �������F���-������������������������������������� ������P������������������������������� ���@���������������Y������ �������F���/�����������������f��������������������p������P�����������������������������b�� ���@���������������Y������ �������F���1�������������������������������������������P������������������������������� ���@���������������Y������ �������F���3�����������������t��������������������!������P�����������������������������p�� ���@����������������Z������ �������F���5�������������������������������������`"������P������������������������������� ���@��������������� Z������ �������F���7�������������������������������������#������P�����������������������������~�� ���@���������������@Z������ �������F���9����������������� ���������������������%������P������������������������������� ���@���������������`Z������ �������F���;��������������������������������������P&������P�������������������������������� ���@���������������Z������ �������F���=�����������������4���������������������'������ ���������������������������������������������������'������������������������������������P����������������������'������ �����������������������������L��� ���@���������������Z������ �������F���A����������������� ����������������������4������L�������������������������������� ���@���������������Z������@������F���C�����������������$���Lo����������������u������ ������������������������������D����������������������P������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/manyprogs.c�������������������������������������������0000664�0000000�0000000�00000003250�15202436720�0023173�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file is used for benchmarking NewCollection(). */ #include "../btf/testdata/bpf_core_read.h" #include "common.h" char __license[] __section("license") = "Dual MIT/GPL"; struct bpf_map_def __section("maps") kprobe_map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 128, }; #pragma clang attribute push(__attribute__((preserve_access_index)), apply_to = record) struct ns_common { unsigned int inum; }; struct mnt_namespace { struct ns_common ns; }; struct nsproxy { struct mnt_namespace *mnt_ns; }; struct task_struct { struct nsproxy *nsproxy; }; #pragma clang attribute pop static inline int impl() { uint64_t initval = 1, *valp; struct task_struct *task = (struct task_struct *)bpf_get_current_task(); uint32_t mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum); valp = bpf_map_lookup_elem(&kprobe_map, &mntns); if (!valp) { bpf_map_update_elem(&kprobe_map, &mntns, &initval, 0); return 0; } __sync_fetch_and_add(valp, 1); return 0; } #define DEFINE_PROBE(i) \ __section("kprobe/sys_execvea" #i) int kprobe_execve##i() { \ return impl(); \ } DEFINE_PROBE(0); DEFINE_PROBE(1); DEFINE_PROBE(2); DEFINE_PROBE(3); DEFINE_PROBE(4); DEFINE_PROBE(5); DEFINE_PROBE(6); DEFINE_PROBE(7); DEFINE_PROBE(8); DEFINE_PROBE(9); DEFINE_PROBE(10); DEFINE_PROBE(11); DEFINE_PROBE(12); DEFINE_PROBE(13); DEFINE_PROBE(14); DEFINE_PROBE(15); DEFINE_PROBE(16); DEFINE_PROBE(17); DEFINE_PROBE(18); DEFINE_PROBE(19); DEFINE_PROBE(20); DEFINE_PROBE(21); DEFINE_PROBE(22); DEFINE_PROBE(23); DEFINE_PROBE(24); DEFINE_PROBE(25); DEFINE_PROBE(26); DEFINE_PROBE(27); DEFINE_PROBE(28); DEFINE_PROBE(29); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/map_spin_lock-eb.elf����������������������������������0000664�0000000�0000000�00000002300�15202436720�0024675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@��������������������������������������������<��<������������������������ �������������������������������� ����������������������"��������� ���������� ���/��������9����������=��� ��� ���B��������P����������������� ����������������������������� ���T����������Y������@���]���������c��� ������o������ ������}���������������� �int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�hash_elem�cnt�lock�bpf_spin_lock�val�type�key�value�max_entries�spin_lock_map�.maps����������������������������� ����������������� ������L�������.text�.maps�spin_lock_map�.llvm_addrsig�.strtab�.symtab�.rel.BTF�������������������������������������������������������������������������)�������������������������y�������B���������������������������������������������������@�����������������������������������������������������������@������� ��������������������������=��������������������������`��������������������������������9��� �������@��������������h������������������������������oL���������������������x���������������������������������1�������������������������8�������0����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/map_spin_lock-el.elf����������������������������������0000664�0000000�0000000�00000002300�15202436720�0024707�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@�������������������������������������������<��<������������������������ ������������������������������� ����������������������"��������� ���������� ���/��������9����������=��� ��� ���B��������P����������������� ����������������������������� ���T����������Y������@���]���������c��� ������o������ ������}���������������� ����int�__ARRAY_SIZE_TYPE__�uint32_t�unsigned int�hash_elem�cnt�lock�bpf_spin_lock�val�type�key�value�max_entries�spin_lock_map�.maps�������������������������� ������������� �������L�������������.text�.maps�spin_lock_map�.llvm_addrsig�.strtab�.symtab�.rel.BTF����������������������������������������������������������������������)����������������������y������B���������������������������������������������������@�����������������������������������������������������������@������� ������������������������������=����������������������`������������������������������������9��� ���@���������������h������������������������������������Lo���������������x������������������������������������1����������������������8������0�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/map_spin_lock.c���������������������������������������0000664�0000000�0000000�00000000511�15202436720�0023767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises bpf_spin_lock. */ #include "common.h" struct bpf_spin_lock { uint32_t val; }; struct hash_elem { int cnt; struct bpf_spin_lock lock; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, uint32_t); __type(value, struct hash_elem); __uint(max_entries, 2); } spin_lock_map __section(".maps"); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/raw_tracepoint-eb.elf���������������������������������0000664�0000000�0000000�00000003040�15202436720�0025102�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������`�����@�����@� ���������������MIT���������������������������������������� ����������������������������@������������������������&��������� ���� ��������:������>�������� ���B ������������������������������ ��������������� ��������������� ��������bpf_args�args�uint64_t�unsigned long�__ARRAY_SIZE_TYPE__�ctx�int�sched_process_exec�raw_tracepoint/sched_process_exec�/ebpf/testdata/raw_tracepoint.c� return 0;�char�__license�license�������� ����������������0����������U���������� ������U����������w�����0�������������������������������������������������;��������������������"�������������������������������������,�������������@������ �.text�.rel.BTF.ext�.llvm_addrsig�__license�raw_tracepoint/sched_process_exec�.strtab�.symtab�.rel.BTF���������������������������������������������������������������������������N��������������������������������g���������������������������������������������������@����������������������������������,�������������������������@���������������������������������$�������������������������P���������������������������������b��������������������������T��������������������������������^��� �������@������������������������ �������������������� ��������������������������������P����������������������������� �������@��������������������� ��� ��������������������oL������������������������������������������������������V�������������������������`�������`��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/raw_tracepoint-el.elf���������������������������������0000664�0000000�0000000�00000003040�15202436720�0025114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������`����������@�����@� ����������������MIT������������������������������������� ����������������������������@������������������������&��������� ��������� ���:������>��������� ��B����� ������������������������� ��������������� ��������������� �����������bpf_args�args�uint64_t�unsigned long�__ARRAY_SIZE_TYPE__�ctx�int�sched_process_exec�raw_tracepoint/sched_process_exec�/ebpf/testdata/raw_tracepoint.c� return 0;�char�__license�license����� ����������������0����������U���������� ������U����������w������0������������������������������������������������;��������������������"���������������������������������,�������������@������������� �.text�.rel.BTF.ext�.llvm_addrsig�__license�raw_tracepoint/sched_process_exec�.strtab�.symtab�.rel.BTF������������������������������������������������������������������������N����������������������������g���������������������������������������������������@��������������������������������������,���������������������@�������������������������������������$���������������������P�������������������������������������b����������������������T������������������������������������^��� ���@���������������������������� �������������������� ����������������������������P��������������������������������� ���@��������������������� ������� �����������������������Lo���������������������������������������������������V����������������������`������`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/raw_tracepoint.c��������������������������������������0000664�0000000�0000000�00000000405�15202436720�0024174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. */ #include "common.h" char __license[] __section("license") = "MIT"; struct bpf_args { uint64_t args[0]; }; __section("raw_tracepoint/sched_process_exec") int sched_process_exec(struct bpf_args *ctx) { return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/strings-eb.elf����������������������������������������0000664�0000000�0000000�00000005110�15202436720�0023552�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@���������������� ��������������������������a�����������c�������� ���������c����:������0���������������� ��������������@�������������������������MIT�������������������������������������This string is allocated in the string section ���������������A��������������������� �������������������������������� ������������������������������������������������� ���!���������������������� ������0���������� ���&������ ���/��������� ��������� ���<����������A������@���M���������Q��� ������W������������� ���������^ ������������������� ��������)�����������3���������������� ��9�����������������int�__ARRAY_SIZE_TYPE__�custkey�char�uint32_t�unsigned int�type�max_entries�key�value�my_map�filter�xdp�/ebpf/testdata/strings.c� uint32_t *value = bpf_map_lookup_elem(&my_map, KEY);� if (value)� (*value)++;� uint32_t newValue = 1;� bpf_map_update_elem(&my_map, KEY, &newValue, 0);� return 2;�__license�.maps�license�������� �������������|�������������e����������������e����������i�����D���(���i�����H���0���i�����L ���H���i�����L���X���i�����T ���p���i�����X������i����d�������������������������������������������������������������������������������������������)����������������� ���>���������������������������������������������������p��������������������������������������������������,�������������@�������������P�������������`�������������p���������������������������������������������  �.text�.rel.BTF.ext�.maps�filter�.relxdp�my_map�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�.rodata.str1.1�������������������������������������������������������������������������H�������������������������S�������p���������������������������������������������������@����������������������������������%�������������������������@���������������������������������!��� �������@��������������p�������@��� ��������������������@������������������������������������������������������������������������������������������ ��������������������������a����������2���������������������0�������������������������\�������������������������H��������������������������������X��� �������@��������������������� ��� �������������������� �������������������������0������������������������������������ �������@������������������������ ��� �����������������0oL���������������������P���������������������������������P������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/strings-el.elf����������������������������������������0000664�0000000�0000000�00000005110�15202436720�0023564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@�������������������������������������������a�����������c������� ����������c�����������������������������������������������������������������MIT�������������������������������������This string is allocated in the string section �������������A��������������������� ������������������������������� ������������������������������������������������� ���!���������������������� ������0���������� ���&������ ���/��������� ��������� ���<����������A������@���M���������Q��� ������W������������������� ���^����� �������������� ���������)�����������3��������������� ���9�������������������int�__ARRAY_SIZE_TYPE__�custkey�char�uint32_t�unsigned int�type�max_entries�key�value�my_map�filter�xdp�/ebpf/testdata/strings.c� uint32_t *value = bpf_map_lookup_elem(&my_map, KEY);� if (value)� (*value)++;� uint32_t newValue = 1;� bpf_map_update_elem(&my_map, KEY, &newValue, 0);� return 2;�__license�.maps�license����� �������������|�������������e����������������e����������i������D��(���i������H��0���i������ L��H���i������L��X���i������ T��p���i������X�����i�����d������������������������������������������������������������������������������������������)������������� �������>�����������������������������������������������p��������������������������������������������������,�������������@�������������P�������������`�������������p����������������������������������������������������  �.text�.rel.BTF.ext�.maps�filter�.relxdp�my_map�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�.rodata.str1.1����������������������������������������������������������������������H����������������������S������p���������������������������������������������������@��������������������������������������%���������������������@�������������������������������������!��� ���@���������������p������@������� ��������������������@�������������������������������������������������������������������������������������� ������������������������������a������2���������������������0�����������������������������\����������������������H�����������������������������������X��� ���@��������������������� ������� �������������������� ����������������������0��������������������������������������� ���@���������������������������� ��� �����������������0���Lo���������������P������������������������������������P���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/strings.c���������������������������������������������0000664�0000000�0000000�00000001010�15202436720�0022635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char __license[] __section("license") = "MIT"; typedef char custkey[48]; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 2); __type(key, custkey); __type(value, uint32_t); } my_map __section(".maps"); #define KEY "This string is allocated in the string section\n" __section("xdp") int filter() { uint32_t *value = bpf_map_lookup_elem(&my_map, KEY); if (value) (*value)++; else { uint32_t newValue = 1; bpf_map_update_elem(&my_map, KEY, &newValue, 0); } return 2; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/struct_ops-eb.elf�������������������������������������0000664�0000000�0000000�00000003450�15202436720�0024273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@� ���������������GPL���������������������ޭ�������������� �� ������� ����������������� ��� ��������E�������������������������������J��������� ���^������������g����������� �������w��� ���@���~���������������� ���� ���������������� ���� �������������������������������������������� ���������������������������int�test_1�struct_ops/test_1�/ebpf/testdata/struct_ops.c� return 0;�char�__ARRAY_SIZE_TYPE__�_license�bpf_testmod_ops�test_2�data�testmod_ops�.struct_ops.link�license����� ����������������0���������� ���������������� �������������:��0�������������������������������������������������p��������������������C���������������������������������������������������������������������0�������������,�������������@������ �.text�.rel.BTF.ext�testmod_ops�.rel.struct_ops.link�.llvm_addrsig�_license�.strtab�.symtab�.rel.BTF�struct_ops/test_1��������������������������������������������������������������������������L�������������������������k�������w���������������������������������������������������@����������������������������������e�������������������������@���������������������������������D�������������������������P���������������������������������$�������������������������X��������������������������������� ��� �������@������������������������ ��������������������`��������������������������p��������������������������������\��� �������@��������������(������� ��� �������������������� �������������������������P�������P����������������������������� �������@��������������H������� ��� ��� �����������������5oL���������������������h���������������������������������T��������������������������������x��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/struct_ops-el.elf�������������������������������������0000664�0000000�0000000�00000003450�15202436720�0024305�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@� ����������������GPL���������������������ᆳ������������ �� ������������ ������������ ������� ���E�������������������������������J��������� ���^������������g����������� �������w��� ���@���~���������������� ���������� ���������� ��������� ��������������������������������������� ������������������������������int�test_1�struct_ops/test_1�/ebpf/testdata/struct_ops.c� return 0;�char�__ARRAY_SIZE_TYPE__�_license�bpf_testmod_ops�test_2�data�testmod_ops�.struct_ops.link�license�� ����������������0���������� ���������������� �������������:���0������������������������������������������������p��������������������C������������������������������������������������������������������0������������,�������������@������������� �.text�.rel.BTF.ext�testmod_ops�.rel.struct_ops.link�.llvm_addrsig�_license�.strtab�.symtab�.rel.BTF�struct_ops/test_1�����������������������������������������������������������������������L����������������������k������w���������������������������������������������������@��������������������������������������e���������������������@�������������������������������������D���������������������P�������������������������������������$���������������������X������������������������������������� ��� ���@���������������������������� ��������������������`����������������������p������������������������������������\��� ���@���������������(������ ������� �������������������� ����������������������P������P��������������������������������� ���@���������������H������ ������� ��� �����������������5���Lo���������������h������������������������������������T����������������������������x���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/struct_ops.c������������������������������������������0000664�0000000�0000000�00000000527�15202436720�0023365�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" char _license[] __section("license") = "GPL"; struct bpf_testmod_ops { int (*test_1)(void); void (*test_2)(int, int); int data; }; __section("struct_ops/test_1") int test_1(void) { return 0; } __section(".struct_ops.link") struct bpf_testmod_ops testmod_ops = { .test_1 = (void *)test_1, .data = 0xdeadbeef, }; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/subprog_reloc-eb.elf����������������������������������0000664�0000000�0000000�00000006150�15202436720�0024733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF�����������������������������������������@�����@��������c���������*{����*������ ��:������0����������������@��������������������������������c���������{����j������`��:������0����������������&������@�������������������������� ��������������0������@��������������������������&����������������������������������y�������������MIT��������������������������� �� ��#���� ����������������� ��� ��������� ��������\ ����������������������������������������������� ����������������������� ��������� ��� ����� ���@����� ���`����� ��������������� �� ������ �������������� ������������������ ��������int�fp_relocation�xdp�/ebpf/testdata/subprog_reloc.c�__section("xdp") int fp_relocation() {� uint32_t key = 0;� uint64_t val = 1;� bpf_map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0);� bpf_for_each_map_elem(&hash_map, sub_prog, (void *)0, 0);� uint64_t *new_val = bpf_map_lookup_elem(&hash_map, &key);� if (!new_val) {� return *new_val;�}�sub_prog�.text�static int sub_prog() {� uint64_t val = 42;� return 0;�char�__ARRAY_SIZE_TYPE__�__license�bpf_map_def�type�key_size�value_size�max_entries�map_flags�unsigned int�hash_map�license�maps������ �������$���$���������������������������e������������������� ����������6��`����������]��d ���������p��h ���@��������p���h��������x�������������������7����������H�� ��������Z����e������������k��<����������]��@ ����������D ���@��������L���`�������T���������������������������������������������������������������������������<�����������������p���.��������������������%��������������������S������������������������@�������������@�������������h�������������x�������������������������������������0�������������,�������������<�������������P�������������`�������������p��������������������������������������������������������������������������������������������������������������������������������������������(�������.rel.text�.rel.BTF.ext�maps�.relxdp�hash_map�fp_relocation�sub_prog�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF��������������������������������������������������������������������������]�������������������������l�������v���������������������������������������������������@�������p����������������������������� �������@������������������������ ��������������������!������������������������������������������������������������� �������@���������������������@��� ��������������������U������������������������������������������������������������������������������������������������������������������q�������������������������������[��������������������������m��� �������@��������������H������� ��� ���������������������������������������������������8�������������������������� ��� �������@��������������h���������� ��� �����������������EoL���������������������h���������������������������������e�������������������������P���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/subprog_reloc-el.elf����������������������������������0000664�0000000�0000000�00000006150�15202436720�0024745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF����������������������������������������@�����@���������c������*���{������������������������������������������������������������������c���������{����������������������������������b������������������������������������������������������������������������������b����������������������������������y�������������MIT������������������������� �� ��#��������� ������������ ������� ���������� ���\����� ����������������������������������������� ����������������������� ��������� ��� ����� ���@����� ���`����� ��������������� ��� ����� �������������� ������������������ �����������int�fp_relocation�xdp�/ebpf/testdata/subprog_reloc.c�__section("xdp") int fp_relocation() {� uint32_t key = 0;� uint64_t val = 1;� bpf_map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0);� bpf_for_each_map_elem(&hash_map, sub_prog, (void *)0, 0);� uint64_t *new_val = bpf_map_lookup_elem(&hash_map, &key);� if (!new_val) {� return *new_val;�}�sub_prog�.text�static int sub_prog() {� uint64_t val = 42;� return 0;�char�__ARRAY_SIZE_TYPE__�__license�bpf_map_def�type�key_size�value_size�max_entries�map_flags�unsigned int�hash_map�license�maps��� �������$���$����������������������������e������������������ ����������6����`��������]��� d��������p��� h��@���������p��h���������x�������������������7����������H�� ��������Z����e������������k���<��������]��� @���������� D��@���������L��`��������T��������������������������������������������������������������������������<�������������p�������.��������������������%��������������������S��������������������@�������������@�������������h�������������x��������������������������������������0������������,�������������<�������������P�������������`�������������p���������������������������������������������������������������������������������������������������������������������������������������������(�������������.rel.text�.rel.BTF.ext�maps�.relxdp�hash_map�fp_relocation�sub_prog�.llvm_addrsig�__license�.strtab�.symtab�.rel.BTF�����������������������������������������������������������������������]����������������������l������v���������������������������������������������������@�������p��������������������������������� ���@���������������������������� ��������������������!������������������������������������������������������������� ���@���������������������@������� ��������������������U������������������������������������������������������������������������������������������������������������������q����������������������������[�����������������������������m��� ���@���������������H������ ������� ������������������������������������������������8����������������������������� ��� ���@���������������h������������� ��� �����������������E���Lo���������������h������������������������������������e����������������������P���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/subprog_reloc.c���������������������������������������0000664�0000000�0000000�00000001351�15202436720�0024021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* This file excercises the ELF loader. */ #include "common.h" char __license[] __section("license") = "MIT"; struct bpf_map_def hash_map __section("maps") = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(uint32_t), .value_size = sizeof(uint64_t), .max_entries = 1, }; static int sub_prog() { uint32_t key = 0; uint64_t val = 42; bpf_map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0); return 0; } __section("xdp") int fp_relocation() { uint32_t key = 0; uint64_t val = 1; bpf_map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0); bpf_for_each_map_elem(&hash_map, sub_prog, (void *)0, 0); uint64_t *new_val = bpf_map_lookup_elem(&hash_map, &key); if (!new_val) { return -1; } return *new_val; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/variables-eb.elf��������������������������������������0000664�0000000�0000000�00000033700�15202436720�0024037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������������3@�����@�����@���������������� ��� c�������������������� ��� c����������������������������������a���������������������������a���������������������������a����������������������������������y!������U ���� y��������������� �����������������������������������a!������U ���� y�����U� ��� ��������������i!�����U ���� q�����U���� ��������������y�������������������������������������������q��������������������������������� ��������������!����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x��x������ ����������������� ��� ��������� ���������a ��������� ���������z ��������� ��������� ��������� ��������� ����� ���� �������� ����� ���� �������� ��������� ��������* ��������� ������������������������� ������������������������������������������������ ��������������������� ������������������������������@�������������������@��������������� ������!������� �����������������@�����"��������&����� �������� ������#�������������#������%��+�����������������������$���'�����9��������� ��M������ ������� ������$��������������)���'�� ���\������*�����f�����������q����������������������������v������������������|���������+������ ������������,����������������������������(������� �������������������������������������int�set_vars�socket�/ebpf/testdata/variables.c� hidden = 0xbeef1;� weak = 0xbeef2;� return 0;�get_bss� return var_bss;�get_data� return var_data;�get_rodata� return var_rodata;�check_struct�__section("socket") int check_struct() {� return var_struct.a == 0xa && var_struct.b == 0xb;�check_struct_pad�__section("socket") int check_struct_pad() {� return 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;�check_array� return var_array[sizeof(var_array) - 1] == 0xff;�add_atomic�__section("socket") int add_atomic() {� __sync_fetch_and_add(&var_atomic, 1);�uint32_t�unsigned int�hidden�weak�var_bss�var_data�var_rodata�var_struct_t�a�b�uint64_t�unsigned long�var_struct�var_struct_pad_t�c�d�e�uint16_t�unsigned short�uint8_t�unsigned char�__ARRAY_SIZE_TYPE__�var_struct_pad�var_array�var_atomic�.bss�.data�.data.array�.data.atomic�.data.struct�.data.weak�.rodata�������� �������L���L����h�����������������������P������p��������� ������ ������ �������������������!����������0��0 ��� ������C��4 ���@������V��8���P������i��L ���`������i��L���p��������\ �����������\�����������l �����������l���������������������������������������������+�����������-�������������������/����������\���� �����\��!��(�����\��3��0�����\��<��8�����\��N��P�����\��W��X�����\��Z��`�����\��u��h�����\��ć�������\��ĉ�������\����������� ���������*����������������5����������\����������V�������������������������������������������������������{�����������������P����������������������!��������������������f���������P������� ���n���������������������������p������� �������������������������������������� ����������������������L����������������P���A�� ��������������������������������������� �������������� ��� ���������������8����� �������������� �������������������0����� ������������������������������������ �������������P�������������p���������������� ������������� ���������������������8������������h������������������������������������������������������������������������������������4������������L��� ���������X������������p��������������� ����������,�������������4�������������<�������������D�������������L�������������T�������������\�������������d�������������x��������������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������  �var_array�check_array�.data.array�.text�.rel.BTF.ext�.relsocket�var_struct�check_struct�.data.struct�get_bss�var_bss�.bss�set_vars�hidden�.data.weak�.llvm_addrsig�var_struct_pad�check_struct_pad�var_atomic�add_atomic�.data.atomic�.strtab�.symtab�get_rodata�var_rodata�.rodata�get_data�var_data�.data�.rel.BTF����������������������������������������������������������������������������������������������2 ������6��������������������������#�������������������������@����������������������������������:�������������������������@��������������������������������6��� �������@��������������.(������������������������������v������������������������H���������������������������������������������������������H��������������������������������'������������������������L�������������������������������� ������������������������P���������������������������������Y������������������������X�������0�������������������������������������������������������� ���������������������������������������������������"��������������������������������1�������������������������"������E�������������������������-��� �������@��������������.������������� �����������������-�������������������������)��������������������������������)��� �������@��������������/h�����������������������������oL���������������������1����������������������������������������������������������,`������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/variables-el.elf��������������������������������������0000664�0000000�0000000�00000033700�15202436720�0024051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ELF������������������������������@3����������@�����@������������������� �c!���������������������� �c!����������������������������������a���������������������������a���������������������������a����������������������������������y������U� ���y������������ ��������������������������������������a������U� ���y�����U � �����������������i�����U� ���q�����U� �����������������y�������������������������������������������q�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������x��x����������� ������������ ������� ���������� ���a����� ���������� ���z����� ���������� �������� ���������� �������� ���������� ������� ���������� ������� ���������� ���*���� ���������� ������������������� ������������������������������������������������������ ��������������������� ������������������������@�������������������@��������������������� !������� �����������������@�����"��������&������ �������� �����#��������������#�����%���+����������������������$���'������9�������� ���M����� ������������� $��������������)���'���� ��\�����*������f�����������q����������������������������v������������������|��������+�������� ����������,����������������������������(������� ����������������������������������������int�set_vars�socket�/ebpf/testdata/variables.c� hidden = 0xbeef1;� weak = 0xbeef2;� return 0;�get_bss� return var_bss;�get_data� return var_data;�get_rodata� return var_rodata;�check_struct�__section("socket") int check_struct() {� return var_struct.a == 0xa && var_struct.b == 0xb;�check_struct_pad�__section("socket") int check_struct_pad() {� return 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;�check_array� return var_array[sizeof(var_array) - 1] == 0xff;�add_atomic�__section("socket") int add_atomic() {� __sync_fetch_and_add(&var_atomic, 1);�uint32_t�unsigned int�hidden�weak�var_bss�var_data�var_rodata�var_struct_t�a�b�uint64_t�unsigned long�var_struct�var_struct_pad_t�c�d�e�uint16_t�unsigned short�uint8_t�unsigned char�__ARRAY_SIZE_TYPE__�var_struct_pad�var_array�var_atomic�.bss�.data�.data.array�.data.atomic�.data.struct�.data.weak�.rodata����� �������L���L�����h����������������������P������p��������� ������ ������ �������������������!����������0��� 0�� ������C��� 4��@������V���8��P������i��� L��`������i���L��p��������� \�����������\����������� l�����������l���������������������������������������������+�����������-�������������������/����������\���� �����\��!��(�����\��3��0�����\��<��8�����\��N��P�����\��W��X�����\��Z��`�����\��u��h�����\���������\���������\����������� ���������*����������������5����������\���������V�������������������������������������������������������{�������������P�����������������������������!�����������������f�����P������� �������n�����������������������p������� �������������������������������������� ��������������������������L������������P�������A���� ��������������������������������������� �������� ������� �����������8����������� ���������� �����������������0����������� ������������������������������ �������������P�������������p����������������������� ������������� ���������������8������������h������������������������������������������������������������������������������������4������������L��������� ���X������������p��������������������� ���,�������������4�������������<�������������D�������������L�������������T�������������\�������������d�������������x���������������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������������������������������������������������������������������������������������������������������������������������������(������������8������������H������������X������������h������������x������������  �var_array�check_array�.data.array�.text�.rel.BTF.ext�.relsocket�var_struct�check_struct�.data.struct�get_bss�var_bss�.bss�set_vars�hidden�.data.weak�.llvm_addrsig�var_struct_pad�check_struct_pad�var_atomic�add_atomic�.data.atomic�.strtab�.symtab�get_rodata�var_rodata�.rodata�get_data�var_data�.data�.rel.BTF���������������������������������������������������������������������������������������� 2������6�����������������������������#���������������������@��������������������������������������:���������������������@������������������������������������6��� ���@���������������(.���������������������������������v���������������������H���������������������������������������������������������H������������������������������������'��������������������L������������������������������������ ��������������������P������������������������������������Y���������������������X������0���������������������������������������������������������� ��������������������������������������������������"������������������������������������1���������������������"������E�����������������������������-�� ���@���������������.���������������� �����������������-����������������������)�����������������������������������)��� ���@���������������h/�����������������������������������Lo���������������1����������������������������������������������������������`,������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/variables.c�������������������������������������������0000664�0000000�0000000�00000003456�15202436720�0023134�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "common.h" // Should not appear in CollectionSpec.Variables. __hidden volatile uint32_t hidden; // Weak variables can be overridden by non-weak symbols when linking BPF // programs using bpftool. Make sure they appear in CollectionSpec.Variables. __weak volatile uint32_t weak __section(".data.weak"); // Ensure vars are referenced so they are not culled by the loader. __section("socket") int set_vars() { hidden = 0xbeef1; weak = 0xbeef2; return 0; } volatile uint32_t var_bss __section(".bss"); __section("socket") int get_bss() { return var_bss; } volatile uint32_t var_data __section(".data"); __section("socket") int get_data() { return var_data; } volatile const uint32_t var_rodata __section(".rodata"); __section("socket") int get_rodata() { return var_rodata; } struct var_struct_t { uint64_t a; uint64_t b; }; volatile struct var_struct_t var_struct __section(".data.struct"); __section("socket") int check_struct() { return var_struct.a == 0xa && var_struct.b == 0xb; } /* Padding before b and after 1-byte-aligned d. */ struct var_struct_pad_t { uint32_t a; uint64_t b; uint16_t c; uint8_t d[5]; uint64_t e; }; volatile struct var_struct_pad_t var_struct_pad __section(".data.struct"); __section("socket") int check_struct_pad() { return 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; } // Variable aligned on page boundary to ensure all bytes in the mapping can be // accessed through the Variable API. volatile uint8_t var_array[8192] __section(".data.array"); __section("socket") int check_array() { return var_array[sizeof(var_array) - 1] == 0xff; } volatile uint32_t var_atomic __section(".data.atomic"); __section("socket") int add_atomic() { __sync_fetch_and_add(&var_atomic, 1); return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/windows/����������������������������������������������0000775�0000000�0000000�00000000000�15202436720�0022502�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/windows/LICENSE���������������������������������������0000664�0000000�0000000�00000002071�15202436720�0023507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MIT License Copyright (c) eBPF for Windows contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/testdata/windows/cgroup_sock_addr.sys��������������������������0000664�0000000�0000000�00000037740�15202436720�0026565�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MZ����������������@��������������������������������������� !L!This program cannot be run in DOS mode. $�������e)d!Hr7!Hr7!Hr7Uq6"Hr7Uv6&Hr7Us6$Hr7!Hs7&Hr7z6 Hr7p6 Hr7Rich!Hr7��������PE��d �g���������"� +�������������������@��������� ��� ��� �������������G���`A�������������������������������������������\��<������������������8����������Pd��T���������������������������c��@�����������`��H���������������������������.text�������������������������� ��hcgroup~4��� ������ �������������� ��hcgroup~3 ���0�������������������� ��hcgroup~2���@�������������������� ��hcgroup~1 ���P�������������������� ��h.rdata�����`������ ��������������@��H.data�������p������(��������������@��.pdata������������,��������������@��Hprograms@���������.��������������@��maps��������������2��������������@��INIT�������������4�������������� ��b.reloc������������6��������������@��B������������������������������������������������������������������������LL$ LD$HT$L$HhH_��H3HD$PH��HL$@�P��HL$@O��HD$0H|$0�u HO��HD$0HD$0HD$8H$���HD$ L$���L$���HT$xL$pHD$8O��HL$PH3,��HhDL$ LD$T$HL$VW|$ t ��*D$0Ht ��D$0H )_��HHt$(3_^HT$HL$WH���H^��H3H$���HD$@����D$H���H4��HD$PHD$X����D$`����HD$h����D$p����HFHD$xDŽ$������H��H$���HDŽ$�������DŽ$�������HDŽ$�������DŽ$�������HDŽ$�������H$���H30���HD$ ����E3LD$@H$��HP3D$0|$0�}L`��3H P��M��D$0|$0�|H$��H (���HHhD$0H$���H3��H���_HL$H8H `��M��D$ |$ ��uH e`��wM��H8LD$HT$HL$HXHF]��H3HD$HD$0����HD$@����HD$8����H=`���t ��CHD$8HD$ LL$@L^��HT$hHL$`L��D$0|$0�t HD$`H_��D$0HL$HH3��HXHL$H_������3HT$HL$H8H{\��H3HD$ $$D$D$D$D$D$D$D$D$yD$ D$ RD$ D$ D$ }D$D$D$D$AD$"D$D$D$D$D$D$D$YD$D$iD$\D$$D$TD$HD$@H $HHD$HH� ���HL$ H3��H8HT$HL$HD$H ��HHD$H����HT$HL$HD$H�����HD$H�����HT$HL$HD$H *{��HHD$H����HL$HD$�����HD$@���HD$@����̹���)��H(MA8HI ������H(H(A�LDLAtA@McPLHcL#IcNHBHHBTtƒLM3I���H(ff�����H; )Z��uHfuH2ff�����ff�����%*J��R�t�l�Q�u�e�r�y�R�e�g�i�s�t�r�y�V�a�l�u�e�s�E�x���P�a�r�a�m�e�t�e�r�s���N�p�i�M�o�d�u�l�e�I�d�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������HT$HL$H��HO��H3H$��HD$X����HD$P����HD$@����HD$`����HD$p����HD$x����HD$h����HD$H����H$��HD$PH$��HD$HHD$@����HD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$P@HD$@HD$HL$@HHD$P@(HD$@HD$HL$@fHHD$P@,HD$@HD$HL$@H(���Hk�H$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���>��HD$X(���Hk�H$��HID tH|$X�u3��HD$HHL$XHHHD$HHD$hHD$hH@HD$hHD$HHD$`HD$`HHD$` ���HkH$��HI HDHD$PHD$hHD$@HD$p����(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���=��HD$X(���HkH$��HID tH|$X�u3��� ���Hk�H$��HI HDHD$PHD$hHD$@(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���=��HD$X(���HkH$��HID t H|$X�u3.HD$XHD$PHD$X���H|$P�u HD$P�HD$XHD$XH$��H3H�����������������������������������������������������������������HT$HL$H��H?��H3H$��HD$X����HD$P����HD$@����HD$`����HD$p����HD$x����HD$h����HD$H����H$��HD$PH$��HD$HHD$@����HD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$P@HD$@HD$HL$@HHD$P@HD$@HD$HL$@HHD$P@ HD$@HD$HL$@HHD$P@$HD$@HD$HL$@HHD$P@(HD$@HD$HL$@fHHD$P@,HD$@HD$HL$@H(���Hk�H$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���l.��HD$X(���Hk�H$��HID tH|$X�u3��HD$HHL$XHHHD$HHD$hHD$hH@HD$hHD$HHD$`HD$`HHD$` ���HkH$��HI HDHD$PHD$hHD$@HD$p����(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���o-��HD$X(���HkH$��HID tH|$X�u3��� ���Hk�H$��HI HDHD$PHD$hHD$@(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���,��HD$X(���HkH$��HID t H|$X�u3.HD$XHD$PHD$X���H|$P�u HD$P�HD$XHD$XH$��H3H����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������HT$HL$H��H/��H3H$��HD$X����HD$P����HD$@����HD$`����HD$p����HD$x����HD$h����HD$H����H$��HD$PH$��HD$HHD$@����HD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$P@HD$@HD$HL$@HHD$P@(HD$@HD$HL$@fHHD$P@,HD$@HD$HL$@H(���Hk�H$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$�����HD$X(���Hk�H$��HID tH|$X�u3��HD$HHL$XHHHD$HHD$hHD$hH@HD$hHD$HHD$`HD$`HHD$` ���HkH$��HI HDHD$PHD$hHD$@HD$p����(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$�����HD$X(���HkH$��HID tH|$X�u3��� ���HkH$��HI HDHD$PHD$hHD$@(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$�����HD$X(���HkH$��HID t H|$X�u3.HD$XHD$PHD$X���H|$P�u HD$P�HD$XHD$XH$��H3H�����������������������������������������������������������������HT$HL$H��H��H3H$��HD$X����HD$P����HD$@����HD$`����HD$p����HD$x����HD$h����HD$H����H$��HD$PH$��HD$HHD$@����HD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$HHL$@HHHD$P@HD$@HD$HL$@HHD$P@HD$@HD$HL$@HHD$P@ HD$@HD$HL$@HHD$P@$HD$@HD$HL$@HHD$P@(HD$@HD$HL$@fHHD$P@,HD$@HD$HL$@H(���Hk�H$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���l��HD$X(���Hk�H$��HID tH|$X�u3��HD$HHL$XHHHD$HHD$hHD$hH@HD$hHD$HHD$`HD$`HHD$` ���HkH$��HI HDHD$PHD$hHD$@HD$p����(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$���o ��HD$X(���HkH$��HID tH|$X�u3��� ���HkH$��HI HDHD$PHD$hHD$@(���HkH$��HIHDH$���H$��HD$(HD$xHD$ LL$pLD$`HT$@HL$PH$��� ��HD$X(���HkH$��HID t H|$X�u3.HD$XHD$PHD$X���H|$P�u HD$P�HD$XHD$XH$��H3H����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������6��������������������l������������T��������������0�@�����@���0�@��� �@��� �@�������������`����P��p��`�������� ��0��� ���0���@���P�����(`������egress_connection_policy_map����ingress_connection_policy_map���socket_cookie_map�������helper_id_26����helper_id_2�����helper_id_1�����cgroup~4��������cgroup/connect4�authorize_connect4��SHA256������cgroup~3��������cgroup/connect6�authorize_connect6������cgroup~2��������cgroup/recv_accept4�����authorize_recv_accept4��cgroup~1��������cgroup/recv_accept6�����authorize_recv_accept6��DD/\Svu# \!rDD/\Svu# \!rDD/\Svu# \!r��H������@���P�@�������������(�����@r�@���p�@�������������������DD/\Svu# \!r��������@���������������������������������������������������������������������������������������p�@�������������������H`�@���P`�@���x`�@�����������E�������������`�@������������������������������������������������������������������������������������������������������������������X`�@���``�@���h`�@���p`�@���������������g�������^����e���%������g���� �����`e��`%������g����������Dg��D'����������������������������������������������������������������������������������������������RSDSѺW2N!���D:\a\ebpf-for-windows\ebpf-for-windows\x64\Debug\cgroup_sock_addr.pdb���GCTL�����.text$mn������p���.text$mn$00�0��x���.text$s�� ����cgroup~4�����0�� ��cgroup~3�����@����cgroup~2�����P�� ��cgroup~1�����`��H���.idata$5����H`��0���.00cfg��x`��@���.gfids��`�����.giats��`��@��.rdata���e��H��.rdata$zzzdbg���Hg�����.xdata���p����.data���r�����.bss����������.pdata�����@��programs����������maps�������\���INIT����\��(���.idata$2���������.idata$3������H���.idata$4���������.idata$6�����������������������������������������������&����@��P�����p`$�� � p��@����� �� b��"����@��H�����b��@�� ���#��Y�@���� � 4� 2p���������������B��������������2-+��] f����������������������������������(�������(��������������a�@����������(�������(��������������(a�@����������(�������(��������������8a�@���90`$90`$������������(�������(��������������a�@����������(�������(��������������(a�@����������(�������(��������������8a�@������������������(�������(��������������a�@����������(�������(��������������(a�@����������(�������(��������������8a�@���90`$8��������@���`�@���p�@����@����@����@���7.0`$7.0`$7.0`$ȪGSKK90`$7.0`$�������(�������(��������������a�@����������(�������(��������������(a�@����������(�������(��������������8a�@������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Hg�������\g�����Y��hg��`����g����B��g��p��V��g��@��^��g��`����g������g�������g�� ��&��g��� ��#��g���0�� 4��g���@��C��g���P�� T��g�����,��g���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �@���Ha�@���Xa�@���ha�@���(p�@����������p�@����������"�������p�@���0r�@���b�@��� �������|a�@���������������������������������0�@���a�@���a�@���a�@���p�@����������pr�@����������(�������Pr�@���`r�@���b�@��� �������|a�@���������������������������������@�@���a�@���a�@���a�@���,p�@����������Pq�@����������"�������p�@��� r�@���`b�@��� �������|a�@���������������������������������P�@����b�@���b�@���(b�@���p�@����������0p�@����������(�������q�@���r�@���@b�@��� �������|a�@������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������P�������P����������8������������������������`�@��������������������������P�������P����������8������������������������`�@��������������������������P�������P����������8������������������������a�@�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������H\$WH HH���HH`H\$0H _HͿHtH2-+��H;t HH̹���)̘����������J���`��������������(`����������������������������������6��������������������l������������T��������������NmrDeregisterClient�NmrClientAttachProvider�NmrWaitForClientDeregisterComplete��NmrRegisterClient�NETIO.SYS�: RtlInitUnicodeString�� RtlQueryRegistryValues�� MmGetSystemRoutineAddress�ntoskrnl.exe�����������������������������������������������������������������������������������`��,���HPX`hȢТh(08@�p��,���Px@p����X��� (08@PhpxȠР�@HPX`pСء� (8������H��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 *H 010  `He�0 +70|0E +705�.*մ$fڎ`10  +71����r*o@MUq!R!<geu}���7Q[L> h`?|Űv lU�[� ��a+luc:AnM6[jO���)]1Ԧ]&`Se8n*���)S qcn DNj#s(2J��� Up`Y\*r uu� ��Pbݠ*$I.lL))bWWej�(��R8\e g &"7*.Fj|RE�,��Z>+~Y}rOҋ2\Ѕڎ�.��fJi?Pqz+7ʅryVm�2��IC7)Zp'") Ҹ\ B"�4��=iX#"3c1,OPt :\'�6��E :"*~ư Ƴn9Wy,>+l;�8����������������������������������010  `He� N0mj "wO㵏\,;g~Zt900�EH6K[G0  *H �051301U*WDKTestCert runneradmin,1338723964078332160 250323214041Z 350323000000Z051301U*WDKTestCert runneradmin,1338723964078332160"0  *H ��0 �HѸo_. ^iW,wQ,K)g& �XQ_zKҐg$ Ŵ~0.͋P= k"}k-ۙ�V阵rf kYJN9T˹lG&pA60i^'gB0!Gf\˖ /31.EIG7yB};4ꭏణdGxvfÆ#%FlEY81cZ�$0"0 U00U% 0 +0  *H ��Ƥû(دM{I,~'N<f} qݪ1($Zbtw÷4v;# fU0soea3WΞCǏlo쌾̤^ q+ŏ$%&=?;"yN21̭~ #sdZBcPEuM1'-snYF,\JLAۣAXi=ϘYܢƂ7?100I051301U*WDKTestCert runneradmin,133872396407833216EH6K[G0  `He�|0 +7 10�0 *H  1  +70 +7 10  +70/ *H  1" Y jO>jX; 1bw8j0  *H ��%:|m}cGs~Acp~tg,9zFxezt eF.DXgE\k̰w %G稣)XNy@00"7oI)<ۋwUl,}j>z$].?� n3`kԘp6NSYFߢ-^z;;UvKEb20D?Y~XjeXI *rNY�����������������������������������golang-github-cilium-ebpf-0.21.0+ds1/types.go�������������������������������������������������������0000664�0000000�0000000�00000041140�15202436720�0020672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "github.com/cilium/ebpf/internal/platform" "github.com/cilium/ebpf/internal/sys" ) //go:generate go tool stringer -output types_string.go -type=MapType,ProgramType,PinType // MapType indicates the type map structure // that will be initialized in the kernel. type MapType uint32 // All the various map types that can be created const ( UnspecifiedMap MapType = MapType(platform.LinuxTag | iota) // Hash is a hash map Hash // Array is an array map Array // ProgramArray - A program array map is a special kind of array map whose map // values contain only file descriptors referring to other eBPF // programs. Thus, both the key_size and value_size must be // exactly four bytes. This map is used in conjunction with the // TailCall helper. ProgramArray // PerfEventArray - A perf event array is used in conjunction with PerfEventRead // and PerfEventOutput calls, to read the raw bpf_perf_data from the registers. PerfEventArray // PerCPUHash - This data structure is useful for people who have high performance // network needs and can reconcile adds at the end of some cycle, so that // hashes can be lock free without the use of XAdd, which can be costly. PerCPUHash // PerCPUArray - This data structure is useful for people who have high performance // network needs and can reconcile adds at the end of some cycle, so that // hashes can be lock free without the use of XAdd, which can be costly. // Each CPU gets a copy of this hash, the contents of all of which can be reconciled // later. PerCPUArray // StackTrace - This holds whole user and kernel stack traces, it can be retrieved with // GetStackID StackTrace // CGroupArray - This is a very niche structure used to help SKBInCGroup determine // if an skb is from a socket belonging to a specific cgroup CGroupArray // LRUHash - This allows you to create a small hash structure that will purge the // least recently used items rather than throw an error when you run out of memory LRUHash // LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs, // it has more to do with including the CPU id with the LRU calculation so that if a // particular CPU is using a value over-and-over again, then it will be saved, but if // a value is being retrieved a lot but sparsely across CPUs it is not as important, basically // giving weight to CPU locality over overall usage. LRUCPUHash // LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful, // for storing things like IP addresses which can be bit masked allowing for keys of differing // values to refer to the same reference based on their masks. See wikipedia for more details. LPMTrie // ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps // itself. ArrayOfMaps // HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps // itself. HashOfMaps // DevMap - Specialized map to store references to network devices. DevMap // SockMap - Specialized map to store references to sockets. SockMap // CPUMap - Specialized map to store references to CPUs. CPUMap // XSKMap - Specialized map for XDP programs to store references to open sockets. XSKMap // SockHash - Specialized hash to store references to sockets. SockHash // CGroupStorage - Special map for CGroups. CGroupStorage // ReusePortSockArray - Specialized map to store references to sockets that can be reused. ReusePortSockArray // PerCPUCGroupStorage - Special per CPU map for CGroups. PerCPUCGroupStorage // Queue - FIFO storage for BPF programs. Queue // Stack - LIFO storage for BPF programs. Stack // SkStorage - Specialized map for local storage at SK for BPF programs. SkStorage // DevMapHash - Hash-based indexing scheme for references to network devices. DevMapHash // StructOpsMap - This map holds a kernel struct with its function pointer implemented in a BPF // program. StructOpsMap // RingBuf - Similar to PerfEventArray, but shared across all CPUs. RingBuf // InodeStorage - Specialized local storage map for inodes. InodeStorage // TaskStorage - Specialized local storage map for task_struct. TaskStorage // BloomFilter - Space-efficient data structure to quickly test whether an element exists in a set. BloomFilter // UserRingbuf - The reverse of RingBuf, used to send messages from user space to BPF programs. UserRingbuf // CgroupStorage - Store data keyed on a cgroup. If the cgroup disappears, the key is automatically removed. CgroupStorage // Arena - Sparse shared memory region between a BPF program and user space. Arena ) // Map types (Windows). const ( WindowsHash MapType = MapType(platform.WindowsTag | iota + 1) WindowsArray WindowsProgramArray WindowsPerCPUHash WindowsPerCPUArray WindowsHashOfMaps WindowsArrayOfMaps WindowsLRUHash WindowsLPMTrie WindowsQueue WindowsLRUCPUHash WindowsStack WindowsRingBuf ) // MapTypeForPlatform returns a platform specific map type. // // Use this if the library doesn't provide a constant yet. func MapTypeForPlatform(plat string, typ uint32) (MapType, error) { return platform.EncodeConstant[MapType](plat, typ) } // hasPerCPUValue returns true if the Map stores a value per CPU. func (mt MapType) hasPerCPUValue() bool { switch mt { case PerCPUHash, PerCPUArray, LRUCPUHash, PerCPUCGroupStorage: return true case WindowsPerCPUHash, WindowsPerCPUArray, WindowsLRUCPUHash: return true default: return false } } // canStoreMapOrProgram returns true if the Map stores references to another Map // or Program. func (mt MapType) canStoreMapOrProgram() bool { return mt.canStoreMap() || mt.canStoreProgram() || mt == StructOpsMap } // canStoreMap returns true if the map type accepts a map fd // for update and returns a map id for lookup. func (mt MapType) canStoreMap() bool { return mt == ArrayOfMaps || mt == HashOfMaps || mt == WindowsArrayOfMaps || mt == WindowsHashOfMaps } // canStoreProgram returns true if the map type accepts a program fd // for update and returns a program id for lookup. func (mt MapType) canStoreProgram() bool { return mt == ProgramArray || mt == WindowsProgramArray } // canHaveValueSize returns true if the map type supports setting a value size. func (mt MapType) canHaveValueSize() bool { switch mt { case RingBuf, Arena: return false // Special-case perf events since they require a value size of either 0 or 4 // for historical reasons. Let the library fix this up later. case PerfEventArray: return false } return true } // mustHaveNoPrealloc returns true if the map type does not support // preallocation and needs the BPF_F_NO_PREALLOC flag set to be created // successfully. func (mt MapType) mustHaveNoPrealloc() bool { switch mt { case CgroupStorage, InodeStorage, TaskStorage, SkStorage: return true case LPMTrie: return true } return false } // ProgramType of the eBPF program type ProgramType uint32 // eBPF program types (Linux). const ( UnspecifiedProgram = ProgramType(sys.BPF_PROG_TYPE_UNSPEC) SocketFilter = ProgramType(sys.BPF_PROG_TYPE_SOCKET_FILTER) Kprobe = ProgramType(sys.BPF_PROG_TYPE_KPROBE) SchedCLS = ProgramType(sys.BPF_PROG_TYPE_SCHED_CLS) SchedACT = ProgramType(sys.BPF_PROG_TYPE_SCHED_ACT) TracePoint = ProgramType(sys.BPF_PROG_TYPE_TRACEPOINT) XDP = ProgramType(sys.BPF_PROG_TYPE_XDP) PerfEvent = ProgramType(sys.BPF_PROG_TYPE_PERF_EVENT) CGroupSKB = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SKB) CGroupSock = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SOCK) LWTIn = ProgramType(sys.BPF_PROG_TYPE_LWT_IN) LWTOut = ProgramType(sys.BPF_PROG_TYPE_LWT_OUT) LWTXmit = ProgramType(sys.BPF_PROG_TYPE_LWT_XMIT) SockOps = ProgramType(sys.BPF_PROG_TYPE_SOCK_OPS) SkSKB = ProgramType(sys.BPF_PROG_TYPE_SK_SKB) CGroupDevice = ProgramType(sys.BPF_PROG_TYPE_CGROUP_DEVICE) SkMsg = ProgramType(sys.BPF_PROG_TYPE_SK_MSG) RawTracepoint = ProgramType(sys.BPF_PROG_TYPE_RAW_TRACEPOINT) CGroupSockAddr = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR) LWTSeg6Local = ProgramType(sys.BPF_PROG_TYPE_LWT_SEG6LOCAL) LircMode2 = ProgramType(sys.BPF_PROG_TYPE_LIRC_MODE2) SkReuseport = ProgramType(sys.BPF_PROG_TYPE_SK_REUSEPORT) FlowDissector = ProgramType(sys.BPF_PROG_TYPE_FLOW_DISSECTOR) CGroupSysctl = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SYSCTL) RawTracepointWritable = ProgramType(sys.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) CGroupSockopt = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SOCKOPT) Tracing = ProgramType(sys.BPF_PROG_TYPE_TRACING) StructOps = ProgramType(sys.BPF_PROG_TYPE_STRUCT_OPS) Extension = ProgramType(sys.BPF_PROG_TYPE_EXT) LSM = ProgramType(sys.BPF_PROG_TYPE_LSM) SkLookup = ProgramType(sys.BPF_PROG_TYPE_SK_LOOKUP) Syscall = ProgramType(sys.BPF_PROG_TYPE_SYSCALL) Netfilter = ProgramType(sys.BPF_PROG_TYPE_NETFILTER) ) // eBPF program types (Windows). // // See https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_structs.h#L170 const ( WindowsXDP ProgramType = ProgramType(platform.WindowsTag) | (iota + 1) WindowsBind WindowsCGroupSockAddr WindowsSockOps WindowsXDPTest ProgramType = ProgramType(platform.WindowsTag) | 998 WindowsSample ProgramType = ProgramType(platform.WindowsTag) | 999 ) // ProgramTypeForPlatform returns a platform specific program type. // // Use this if the library doesn't provide a constant yet. func ProgramTypeForPlatform(plat string, value uint32) (ProgramType, error) { return platform.EncodeConstant[ProgramType](plat, value) } // AttachType of the eBPF program, needed to differentiate allowed context accesses in // some newer program types like CGroupSockAddr. Should be set to AttachNone if not required. // Will cause invalid argument (EINVAL) at program load time if set incorrectly. type AttachType uint32 //go:generate go tool stringer -type AttachType -trimprefix Attach // AttachNone is an alias for AttachCGroupInetIngress for readability reasons. const AttachNone AttachType = 0 // Attach types (Linux). const ( AttachCGroupInetIngress = AttachType(sys.BPF_CGROUP_INET_INGRESS) AttachCGroupInetEgress = AttachType(sys.BPF_CGROUP_INET_EGRESS) AttachCGroupInetSockCreate = AttachType(sys.BPF_CGROUP_INET_SOCK_CREATE) AttachCGroupSockOps = AttachType(sys.BPF_CGROUP_SOCK_OPS) AttachSkSKBStreamParser = AttachType(sys.BPF_SK_SKB_STREAM_PARSER) AttachSkSKBStreamVerdict = AttachType(sys.BPF_SK_SKB_STREAM_VERDICT) AttachCGroupDevice = AttachType(sys.BPF_CGROUP_DEVICE) AttachSkMsgVerdict = AttachType(sys.BPF_SK_MSG_VERDICT) AttachCGroupInet4Bind = AttachType(sys.BPF_CGROUP_INET4_BIND) AttachCGroupInet6Bind = AttachType(sys.BPF_CGROUP_INET6_BIND) AttachCGroupInet4Connect = AttachType(sys.BPF_CGROUP_INET4_CONNECT) AttachCGroupInet6Connect = AttachType(sys.BPF_CGROUP_INET6_CONNECT) AttachCGroupInet4PostBind = AttachType(sys.BPF_CGROUP_INET4_POST_BIND) AttachCGroupInet6PostBind = AttachType(sys.BPF_CGROUP_INET6_POST_BIND) AttachCGroupUDP4Sendmsg = AttachType(sys.BPF_CGROUP_UDP4_SENDMSG) AttachCGroupUDP6Sendmsg = AttachType(sys.BPF_CGROUP_UDP6_SENDMSG) AttachLircMode2 = AttachType(sys.BPF_LIRC_MODE2) AttachFlowDissector = AttachType(sys.BPF_FLOW_DISSECTOR) AttachCGroupSysctl = AttachType(sys.BPF_CGROUP_SYSCTL) AttachCGroupUDP4Recvmsg = AttachType(sys.BPF_CGROUP_UDP4_RECVMSG) AttachCGroupUDP6Recvmsg = AttachType(sys.BPF_CGROUP_UDP6_RECVMSG) AttachCGroupGetsockopt = AttachType(sys.BPF_CGROUP_GETSOCKOPT) AttachCGroupSetsockopt = AttachType(sys.BPF_CGROUP_SETSOCKOPT) AttachTraceRawTp = AttachType(sys.BPF_TRACE_RAW_TP) AttachTraceFEntry = AttachType(sys.BPF_TRACE_FENTRY) AttachTraceFExit = AttachType(sys.BPF_TRACE_FEXIT) AttachModifyReturn = AttachType(sys.BPF_MODIFY_RETURN) AttachLSMMac = AttachType(sys.BPF_LSM_MAC) AttachTraceIter = AttachType(sys.BPF_TRACE_ITER) AttachCgroupInet4GetPeername = AttachType(sys.BPF_CGROUP_INET4_GETPEERNAME) AttachCgroupInet6GetPeername = AttachType(sys.BPF_CGROUP_INET6_GETPEERNAME) AttachCgroupInet4GetSockname = AttachType(sys.BPF_CGROUP_INET4_GETSOCKNAME) AttachCgroupInet6GetSockname = AttachType(sys.BPF_CGROUP_INET6_GETSOCKNAME) AttachXDPDevMap = AttachType(sys.BPF_XDP_DEVMAP) AttachCgroupInetSockRelease = AttachType(sys.BPF_CGROUP_INET_SOCK_RELEASE) AttachXDPCPUMap = AttachType(sys.BPF_XDP_CPUMAP) AttachSkLookup = AttachType(sys.BPF_SK_LOOKUP) AttachXDP = AttachType(sys.BPF_XDP) AttachSkSKBVerdict = AttachType(sys.BPF_SK_SKB_VERDICT) AttachSkReuseportSelect = AttachType(sys.BPF_SK_REUSEPORT_SELECT) AttachSkReuseportSelectOrMigrate = AttachType(sys.BPF_SK_REUSEPORT_SELECT_OR_MIGRATE) AttachPerfEvent = AttachType(sys.BPF_PERF_EVENT) AttachTraceKprobeMulti = AttachType(sys.BPF_TRACE_KPROBE_MULTI) AttachTraceKprobeSession = AttachType(sys.BPF_TRACE_KPROBE_SESSION) AttachLSMCgroup = AttachType(sys.BPF_LSM_CGROUP) AttachStructOps = AttachType(sys.BPF_STRUCT_OPS) AttachNetfilter = AttachType(sys.BPF_NETFILTER) AttachTCXIngress = AttachType(sys.BPF_TCX_INGRESS) AttachTCXEgress = AttachType(sys.BPF_TCX_EGRESS) AttachTraceUprobeMulti = AttachType(sys.BPF_TRACE_UPROBE_MULTI) AttachCgroupUnixConnect = AttachType(sys.BPF_CGROUP_UNIX_CONNECT) AttachCgroupUnixSendmsg = AttachType(sys.BPF_CGROUP_UNIX_SENDMSG) AttachCgroupUnixRecvmsg = AttachType(sys.BPF_CGROUP_UNIX_RECVMSG) AttachCgroupUnixGetpeername = AttachType(sys.BPF_CGROUP_UNIX_GETPEERNAME) AttachCgroupUnixGetsockname = AttachType(sys.BPF_CGROUP_UNIX_GETSOCKNAME) AttachNetkitPrimary = AttachType(sys.BPF_NETKIT_PRIMARY) AttachNetkitPeer = AttachType(sys.BPF_NETKIT_PEER) ) // Attach types (Windows). // // See https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_structs.h#L260 const ( AttachWindowsXDP = AttachType(platform.WindowsTag | iota + 1) AttachWindowsBind AttachWindowsCGroupInet4Connect AttachWindowsCGroupInet6Connect AttachWindowsCgroupInet4RecvAccept AttachWindowsCgroupInet6RecvAccept AttachWindowsCGroupSockOps AttachWindowsSample AttachWindowsXDPTest ) // AttachTypeForPlatform returns a platform specific attach type. // // Use this if the library doesn't provide a constant yet. func AttachTypeForPlatform(plat string, value uint32) (AttachType, error) { return platform.EncodeConstant[AttachType](plat, value) } // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command type AttachFlags uint32 // PinType determines whether a map is pinned into a BPFFS. type PinType uint32 // Valid pin types. // // Mirrors enum libbpf_pin_type. const ( PinNone PinType = iota // Pin an object by using its name as the filename. PinByName ) // LoadPinOptions control how a pinned object is loaded. type LoadPinOptions struct { // Request a read-only or write-only object. The default is a read-write // object. Only one of the flags may be set. ReadOnly bool WriteOnly bool // Raw flags for the syscall. Other fields of this struct take precedence. Flags uint32 } // Marshal returns a value suitable for BPF_OBJ_GET syscall file_flags parameter. func (lpo *LoadPinOptions) Marshal() uint32 { if lpo == nil { return 0 } flags := lpo.Flags if lpo.ReadOnly { flags |= sys.BPF_F_RDONLY } if lpo.WriteOnly { flags |= sys.BPF_F_WRONLY } return flags } // BatchOptions batch map operations options // // Mirrors libbpf struct bpf_map_batch_opts // Currently BPF_F_FLAG is the only supported // flag (for ElemFlags). type BatchOptions struct { ElemFlags uint64 Flags uint64 } // LogLevel controls the verbosity of the kernel's eBPF program verifier. // These constants can be used for the ProgramOptions.LogLevel field. type LogLevel = sys.LogLevel const ( // Print verifier state at branch points. LogLevelBranch = sys.BPF_LOG_LEVEL1 // Print verifier state for every instruction. // Available since Linux v5.2. LogLevelInstruction = sys.BPF_LOG_LEVEL2 // Print verifier errors and stats at the end of the verification process. // Available since Linux v5.2. LogLevelStats = sys.BPF_LOG_STATS ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/types_string.go������������������������������������������������0000664�0000000�0000000�00000012717�15202436720�0022270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,PinType"; DO NOT EDIT. package ebpf import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[UnspecifiedMap-0] _ = x[Hash-1] _ = x[Array-2] _ = x[ProgramArray-3] _ = x[PerfEventArray-4] _ = x[PerCPUHash-5] _ = x[PerCPUArray-6] _ = x[StackTrace-7] _ = x[CGroupArray-8] _ = x[LRUHash-9] _ = x[LRUCPUHash-10] _ = x[LPMTrie-11] _ = x[ArrayOfMaps-12] _ = x[HashOfMaps-13] _ = x[DevMap-14] _ = x[SockMap-15] _ = x[CPUMap-16] _ = x[XSKMap-17] _ = x[SockHash-18] _ = x[CGroupStorage-19] _ = x[ReusePortSockArray-20] _ = x[PerCPUCGroupStorage-21] _ = x[Queue-22] _ = x[Stack-23] _ = x[SkStorage-24] _ = x[DevMapHash-25] _ = x[StructOpsMap-26] _ = x[RingBuf-27] _ = x[InodeStorage-28] _ = x[TaskStorage-29] _ = x[BloomFilter-30] _ = x[UserRingbuf-31] _ = x[CgroupStorage-32] _ = x[Arena-33] _ = x[WindowsHash-268435457] _ = x[WindowsArray-268435458] _ = x[WindowsProgramArray-268435459] _ = x[WindowsPerCPUHash-268435460] _ = x[WindowsPerCPUArray-268435461] _ = x[WindowsHashOfMaps-268435462] _ = x[WindowsArrayOfMaps-268435463] _ = x[WindowsLRUHash-268435464] _ = x[WindowsLPMTrie-268435465] _ = x[WindowsQueue-268435466] _ = x[WindowsLRUCPUHash-268435467] _ = x[WindowsStack-268435468] _ = x[WindowsRingBuf-268435469] } const ( _MapType_name_0 = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStorageBloomFilterUserRingbufCgroupStorageArena" _MapType_name_1 = "WindowsHashWindowsArrayWindowsProgramArrayWindowsPerCPUHashWindowsPerCPUArrayWindowsHashOfMapsWindowsArrayOfMapsWindowsLRUHashWindowsLPMTrieWindowsQueueWindowsLRUCPUHashWindowsStackWindowsRingBuf" ) var ( _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} _MapType_index_1 = [...]uint8{0, 11, 23, 42, 59, 77, 94, 112, 126, 140, 152, 169, 181, 195} ) func (i MapType) String() string { switch { case i <= 33: return _MapType_name_0[_MapType_index_0[i]:_MapType_index_0[i+1]] case 268435457 <= i && i <= 268435469: i -= 268435457 return _MapType_name_1[_MapType_index_1[i]:_MapType_index_1[i+1]] default: return "MapType(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[UnspecifiedProgram-0] _ = x[SocketFilter-1] _ = x[Kprobe-2] _ = x[SchedCLS-3] _ = x[SchedACT-4] _ = x[TracePoint-5] _ = x[XDP-6] _ = x[PerfEvent-7] _ = x[CGroupSKB-8] _ = x[CGroupSock-9] _ = x[LWTIn-10] _ = x[LWTOut-11] _ = x[LWTXmit-12] _ = x[SockOps-13] _ = x[SkSKB-14] _ = x[CGroupDevice-15] _ = x[SkMsg-16] _ = x[RawTracepoint-17] _ = x[CGroupSockAddr-18] _ = x[LWTSeg6Local-19] _ = x[LircMode2-20] _ = x[SkReuseport-21] _ = x[FlowDissector-22] _ = x[CGroupSysctl-23] _ = x[RawTracepointWritable-24] _ = x[CGroupSockopt-25] _ = x[Tracing-26] _ = x[StructOps-27] _ = x[Extension-28] _ = x[LSM-29] _ = x[SkLookup-30] _ = x[Syscall-31] _ = x[Netfilter-32] _ = x[WindowsXDP-268435457] _ = x[WindowsBind-268435458] _ = x[WindowsCGroupSockAddr-268435459] _ = x[WindowsSockOps-268435460] _ = x[WindowsXDPTest-268436454] _ = x[WindowsSample-268436455] } const ( _ProgramType_name_0 = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookupSyscallNetfilter" _ProgramType_name_1 = "WindowsXDPWindowsBindWindowsCGroupSockAddrWindowsSockOps" _ProgramType_name_2 = "WindowsXDPTestWindowsSample" ) var ( _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} _ProgramType_index_1 = [...]uint8{0, 10, 21, 42, 56} _ProgramType_index_2 = [...]uint8{0, 14, 27} ) func (i ProgramType) String() string { switch { case i <= 32: return _ProgramType_name_0[_ProgramType_index_0[i]:_ProgramType_index_0[i+1]] case 268435457 <= i && i <= 268435460: i -= 268435457 return _ProgramType_name_1[_ProgramType_index_1[i]:_ProgramType_index_1[i+1]] case 268436454 <= i && i <= 268436455: i -= 268436454 return _ProgramType_name_2[_ProgramType_index_2[i]:_ProgramType_index_2[i+1]] default: return "ProgramType(" + strconv.FormatInt(int64(i), 10) + ")" } } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[PinNone-0] _ = x[PinByName-1] } const _PinType_name = "PinNonePinByName" var _PinType_index = [...]uint8{0, 7, 16} func (i PinType) String() string { idx := int(i) - 0 if i < 0 || idx >= len(_PinType_index)-1 { return "PinType(" + strconv.FormatInt(int64(i), 10) + ")" } return _PinType_name[_PinType_index[idx]:_PinType_index[idx+1]] } �������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/types_windows.go�����������������������������������������������0000664�0000000�0000000�00000003113�15202436720�0022442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "fmt" "os" "golang.org/x/sys/windows" "github.com/cilium/ebpf/internal/efw" "github.com/cilium/ebpf/internal/platform" ) // WindowsProgramTypeForGUID resolves a GUID to a ProgramType. // // The GUID must be in the form of "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}". // // Returns an error wrapping [os.ErrNotExist] if the GUID is not recignized. func WindowsProgramTypeForGUID(guid string) (ProgramType, error) { progTypeGUID, err := windows.GUIDFromString(guid) if err != nil { return 0, fmt.Errorf("parse GUID: %w", err) } rawProgramType, err := efw.EbpfGetBpfProgramType(progTypeGUID) if err != nil { return 0, fmt.Errorf("get program type: %w", err) } if rawProgramType == 0 { return 0, fmt.Errorf("program type not found for GUID %v: %w", guid, os.ErrNotExist) } return ProgramTypeForPlatform(platform.Windows, rawProgramType) } // WindowsAttachTypeForGUID resolves a GUID to an AttachType. // // The GUID must be in the form of "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}". // // Returns an error wrapping [os.ErrNotExist] if the GUID is not recignized. func WindowsAttachTypeForGUID(guid string) (AttachType, error) { attachTypeGUID, err := windows.GUIDFromString(guid) if err != nil { return 0, fmt.Errorf("parse GUID: %w", err) } rawAttachType, err := efw.EbpfGetBpfAttachType(attachTypeGUID) if err != nil { return 0, fmt.Errorf("get attach type: %w", err) } if rawAttachType == 0 { return 0, fmt.Errorf("attach type not found for GUID %v: %w", attachTypeGUID, os.ErrNotExist) } return AttachTypeForPlatform(platform.Windows, rawAttachType) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/types_windows_test.go������������������������������������������0000664�0000000�0000000�00000002054�15202436720�0023504�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "os" "testing" "github.com/go-quicktest/qt" "golang.org/x/sys/windows" ) func TestWindowsProgramTypeForGUID(t *testing.T) { sampleGUID := windows.GUID{ Data1: 0xf788ef4a, Data2: 0x207d, Data3: 0x4dc3, Data4: [...]byte{0x85, 0xcf, 0x0f, 0x2e, 0xa1, 0x07, 0x21, 0x3c}, } _, err := WindowsProgramTypeForGUID("{00000000-0000-0000-0000-000000000001}") qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) programType, err := WindowsProgramTypeForGUID(sampleGUID.String()) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(WindowsSample, programType)) } func TestWindowsAttachTypeForGUID(t *testing.T) { sampleGUID := windows.GUID{ Data1: 0xf788ef4b, Data2: 0x207d, Data3: 0x4dc3, Data4: [...]byte{0x85, 0xcf, 0x0f, 0x2e, 0xa1, 0x07, 0x21, 0x3c}, } _, err := WindowsAttachTypeForGUID("{00000000-0000-0000-0000-000000000001}") qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) attachType, err := WindowsAttachTypeForGUID(sampleGUID.String()) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(AttachWindowsSample, attachType)) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/variable.go����������������������������������������������������0000664�0000000�0000000�00000015012�15202436720�0021312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "encoding/binary" "fmt" "io" "reflect" "slices" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal/sysenc" ) // VariableSpec is a convenience wrapper for modifying global variables of a // CollectionSpec before loading it into the kernel. // // All operations on a VariableSpec's underlying MapSpec are performed in the // host's native endianness. type VariableSpec struct { Name string // Name of the section this variable was allocated in. SectionName string // Offset of the variable within the datasec. Offset uint32 // Byte representation of the variable's value. Value []byte // Type information of the variable. Optional. Type *btf.Var } // Set sets the value of the VariableSpec to the provided input using the host's // native endianness. func (s *VariableSpec) Set(in any) error { size := int(s.Size()) if size == 0 { bs := binary.Size(in) if bs < 0 { return fmt.Errorf("cannot determine binary size of value %v", in) } size = bs } if s.Value == nil { s.Value = make([]byte, size) } buf, err := sysenc.Marshal(in, size) if err != nil { return fmt.Errorf("marshaling value %s: %w", s.Name, err) } buf.CopyTo(s.Value) return nil } // Get writes the value of the VariableSpec to the provided output using the // host's native endianness. // // Returns an error if the variable is not initialized or if the unmarshaling fails. func (s *VariableSpec) Get(out any) error { if s.Value == nil { return fmt.Errorf("variable is not initialized") } if err := sysenc.Unmarshal(out, s.Value); err != nil { return fmt.Errorf("unmarshaling value: %w", err) } return nil } // Size returns the size of the variable in bytes. func (s *VariableSpec) Size() uint32 { if s.Value != nil { return uint32(len(s.Value)) } if s.Type != nil { size, err := btf.Sizeof(s.Type.Type) if err != nil { return 0 } return uint32(size) } return 0 } // Constant returns true if the variable is located in a data section intended // for constant values. func (s *VariableSpec) Constant() bool { return isConstantDataSection(s.SectionName) } func (s *VariableSpec) String() string { return fmt.Sprintf("%s (type=%v, section=%s, offset=%d, size=%d)", s.Name, s.Type, s.SectionName, s.Offset, s.Size()) } // Copy the VariableSpec. func (s *VariableSpec) Copy() *VariableSpec { cpy := *s cpy.Value = slices.Clone(s.Value) if s.Type != nil { cpy.Type = btf.Copy(s.Type).(*btf.Var) } return &cpy } // Variable is a convenience wrapper for modifying global variables of a // Collection after loading it into the kernel. Operations on a Variable are // performed using direct memory access, bypassing the BPF map syscall API. // // On kernels older than 5.5, most interactions with Variable return // [ErrNotSupported]. type Variable struct { name string offset uint32 size uint32 t *btf.Var mm *Memory } func newVariable(name string, offset, size uint32, t *btf.Var, mm *Memory) (*Variable, error) { if mm != nil { if offset+size > mm.Size() { return nil, fmt.Errorf("offset %d(+%d) is out of bounds", offset, size) } } return &Variable{ name: name, offset: offset, size: size, t: t, mm: mm, }, nil } // Size returns the size of the variable. func (v *Variable) Size() uint32 { return v.size } // ReadOnly returns true if the Variable represents a variable that is read-only // after loading the Collection into the kernel. // // On systems without BPF_F_MMAPABLE support, ReadOnly always returns true. func (v *Variable) ReadOnly() bool { if v.mm == nil { return true } return v.mm.ReadOnly() } // Type returns the [btf.Var] representing the variable in its data section. // This is useful for inspecting the variable's decl tags and the type // information of the inner type. // // Returns nil if the original ELF object did not contain BTF information. func (v *Variable) Type() *btf.Var { return v.t } func (v *Variable) String() string { return fmt.Sprintf("%s (type=%v)", v.name, v.t) } // Set the value of the Variable to the provided input. The input must marshal // to the same length as the size of the Variable. func (v *Variable) Set(in any) error { if v.mm == nil { return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported) } if v.ReadOnly() { return fmt.Errorf("variable %s: %w", v.name, ErrReadOnly) } if !v.mm.bounds(v.offset, v.size) { return fmt.Errorf("variable %s: access out of bounds: %w", v.name, io.EOF) } buf, err := sysenc.Marshal(in, int(v.size)) if err != nil { return fmt.Errorf("marshaling value %s: %w", v.name, err) } if _, err := v.mm.WriteAt(buf.Bytes(), int64(v.offset)); err != nil { return fmt.Errorf("writing value to %s: %w", v.name, err) } return nil } // Get writes the value of the Variable to the provided output. The output must // be a pointer to a value whose size matches the Variable. func (v *Variable) Get(out any) error { if v.mm == nil { return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported) } if !v.mm.bounds(v.offset, v.size) { return fmt.Errorf("variable %s: access out of bounds: %w", v.name, io.EOF) } if err := sysenc.Unmarshal(out, v.mm.b[v.offset:v.offset+v.size]); err != nil { return fmt.Errorf("unmarshaling value %s: %w", v.name, err) } return nil } func checkVariable[T any](v *Variable) error { if v.ReadOnly() { return ErrReadOnly } t := reflect.TypeFor[T]() if t.Kind() == reflect.Uintptr && v.size == 8 { // uintptr is 8 bytes on 64-bit and 4 on 32-bit. In BPF/BTF, pointers are // always 8 bytes. For the sake of portability, allow accessing 8-byte BPF // variables as uintptr on 32-bit systems, since the upper 32 bits of the // pointer should be zero anyway. return nil } if uintptr(v.size) != t.Size() { return fmt.Errorf("can't create %d-byte accessor to %d-byte variable: %w", t.Size(), v.size, ErrInvalidType) } return nil } // VariablePointer returns a pointer to a variable of type T backed by memory // shared with the BPF program. Requires building the Go application with -tags // ebpf_unsafe_memory_experiment. // // T must contain only fixed-size, non-Go-pointer types: bools, floats, // (u)int[8-64], arrays, and structs containing them. Structs must embed // [structs.HostLayout]. [ErrInvalidType] is returned if T is not a valid type. func VariablePointer[T comparable](v *Variable) (*T, error) { if err := checkVariable[T](v); err != nil { return nil, fmt.Errorf("variable pointer %s: %w", v.name, err) } return memoryPointer[T](v.mm, v.offset) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golang-github-cilium-ebpf-0.21.0+ds1/variable_test.go�����������������������������������������������0000664�0000000�0000000�00000025031�15202436720�0022353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ebpf import ( "encoding/binary" "runtime" "structs" "sync/atomic" "testing" "time" "unsafe" "github.com/go-quicktest/qt" "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal/testutils" ) func TestVariableSpec(t *testing.T) { file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNil(spec.Variables["hidden"])) qt.Assert(t, qt.IsNotNil(spec.Variables["weak"])) const want uint32 = 12345 // Update a variable in each type of data section (.bss,.data,.rodata) qt.Assert(t, qt.IsNil(spec.Variables["var_bss"].Set(want))) qt.Assert(t, qt.IsNil(spec.Variables["var_data"].Set(want))) qt.Assert(t, qt.IsNil(spec.Variables["var_rodata"].Set(want))) var v uint32 qt.Assert(t, qt.IsNil(spec.Variables["var_bss"].Get(&v))) qt.Assert(t, qt.Equals(v, want)) qt.Assert(t, qt.IsNil(spec.Variables["var_data"].Get(&v))) qt.Assert(t, qt.Equals(v, want)) qt.Assert(t, qt.IsNil(spec.Variables["var_rodata"].Get(&v))) qt.Assert(t, qt.Equals(v, want)) // Composite values. type structT struct { A, B uint64 } qt.Assert(t, qt.IsNil(spec.Variables["var_struct"].Set(&structT{1, 2}))) var s structT qt.Assert(t, qt.IsNil(spec.Variables["var_struct"].Get(&s))) qt.Assert(t, qt.Equals(s, structT{1, 2})) } func TestVariableSpecCopy(t *testing.T) { file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) if err != nil { t.Fatal(err) } cpy := spec.Copy() // Update a variable in a section with only a single variable (.rodata). const want uint32 = 0xfefefefe wantb := []byte{0xfe, 0xfe, 0xfe, 0xfe} // Same byte sequence regardless of endianness qt.Assert(t, qt.IsNil(cpy.Variables["var_rodata"].Set(want))) qt.Assert(t, qt.DeepEquals(cpy.Variables["var_rodata"].Value, wantb)) // Verify that the original underlying MapSpec was not modified. zero := make([]byte, 4) qt.Assert(t, qt.DeepEquals(spec.Maps[".rodata"].Contents[0].Value.([]byte), zero)) // Check that modifications to the VariableSpec's Type don't affect the // underlying MapSpec's type information on either the original or the copy. cpy.Variables["var_rodata"].Type.Name = "modified" spec.Variables["var_rodata"].Type.Name = "modified" qt.Assert(t, qt.Equals(cpy.Maps[".rodata"].Value.(*btf.Datasec).Vars[0].Type.(*btf.Var).Name, "var_rodata")) qt.Assert(t, qt.Equals(spec.Maps[".rodata"].Value.(*btf.Datasec).Vars[0].Type.(*btf.Var).Name, "var_rodata")) } func TestVariableSpecEmptyValue(t *testing.T) { spec := &VariableSpec{ Type: &btf.Var{ Type: &btf.Int{ Size: 4, }, }, } value := uint32(0x12345678) raw, err := binary.Append(nil, internal.NativeEndian, value) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.IsNotNil(spec.Get(new(uint32)))) qt.Assert(t, qt.IsNotNil(spec.Set(uint64(0))), qt.Commentf("Setting a value of incorrect size should fail")) qt.Assert(t, qt.IsNil(spec.Set(value))) qt.Assert(t, qt.DeepEquals(spec.Value, raw)) spec.Value = nil spec.Type = nil qt.Assert(t, qt.IsNil(spec.Set(uint64(0))), qt.Commentf("Setting an empty value without a type should accept any type")) qt.Assert(t, qt.HasLen(spec.Value, 8)) } func mustReturn(tb testing.TB, prog *Program, value uint32) { tb.Helper() ret, _, err := prog.Test(internal.EmptyBPFContext) qt.Assert(tb, qt.IsNil(err)) qt.Assert(tb, qt.Equals(ret, value)) } func TestVariable(t *testing.T) { testutils.SkipIfNotSupported(t, haveMmapableMaps()) file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) obj := struct { GetBSS *Program `ebpf:"get_bss"` GetData *Program `ebpf:"get_data"` CheckStruct *Program `ebpf:"check_struct"` BSS *Variable `ebpf:"var_bss"` Data *Variable `ebpf:"var_data"` Struct *Variable `ebpf:"var_struct"` Array *Variable `ebpf:"var_array"` }{} qt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil))) t.Cleanup(func() { obj.GetBSS.Close() obj.GetData.Close() obj.CheckStruct.Close() }) mustReturn(t, obj.GetBSS, 0) mustReturn(t, obj.GetData, 0) mustReturn(t, obj.CheckStruct, 0) want := uint32(4242424242) qt.Assert(t, qt.IsNil(obj.BSS.Set(want))) mustReturn(t, obj.GetBSS, want) qt.Assert(t, qt.IsNil(obj.Data.Set(want))) mustReturn(t, obj.GetData, want) qt.Assert(t, qt.IsNil(obj.Struct.Set(&struct{ A, B uint64 }{0xa, 0xb}))) mustReturn(t, obj.CheckStruct, 1) // Ensure page-aligned array variable can be accessed in its entirety. arr := make([]byte, obj.Array.Size()) qt.Assert(t, qt.IsNil(obj.Array.Get(arr))) qt.Assert(t, qt.IsNil(obj.Array.Set(arr))) typ := obj.BSS.Type() qt.Assert(t, qt.IsNotNil(typ)) i, ok := btf.As[*btf.Int](typ.Type) qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.Equals(i.Size, 4)) qt.Assert(t, qt.IsNotNil(obj.Data.Type())) qt.Assert(t, qt.IsNotNil(obj.Struct.Type())) } func TestVariableConst(t *testing.T) { testutils.SkipIfNotSupported(t, haveMmapableMaps()) file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) want := uint32(12345) qt.Assert(t, qt.IsNil(spec.Variables["var_rodata"].Set(want))) obj := struct { GetRodata *Program `ebpf:"get_rodata"` Rodata *Variable `ebpf:"var_rodata"` }{} qt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil))) t.Cleanup(func() { obj.GetRodata.Close() }) var got uint32 qt.Assert(t, qt.IsNil(obj.Rodata.Get(&got))) qt.Assert(t, qt.Equals(got, want)) mustReturn(t, obj.GetRodata, want) qt.Assert(t, qt.IsTrue(obj.Rodata.ReadOnly())) qt.Assert(t, qt.ErrorIs(obj.Rodata.Set(want), ErrReadOnly)) } func TestVariableFallback(t *testing.T) { // LoadAndAssign should work on Variable regardless of BPF_F_MMAPABLE support. file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) obj := struct { Data *Variable `ebpf:"var_data"` }{} mustLoadAndAssign(t, spec, &obj, nil) // Expect either success or ErrNotSupported on all systems. u32 := uint32(0) if err := obj.Data.Get(&u32); err != nil { qt.Assert(t, qt.ErrorIs(err, ErrNotSupported)) } if err := obj.Data.Set(&u32); err != nil { qt.Assert(t, qt.ErrorIs(err, ErrNotSupported)) } } func TestVariablePointer(t *testing.T) { testutils.SkipIfNotSupported(t, haveMmapableMaps()) file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) obj := struct { AddAtomic *Program `ebpf:"add_atomic"` CheckStructPad *Program `ebpf:"check_struct_pad"` CheckArray *Program `ebpf:"check_array"` Atomic *Variable `ebpf:"var_atomic"` StructPad *Variable `ebpf:"var_struct_pad"` Array *Variable `ebpf:"var_array"` }{} unsafeMemory = true t.Cleanup(func() { unsafeMemory = false }) qt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil))) t.Cleanup(func() { obj.AddAtomic.Close() obj.CheckStructPad.Close() obj.CheckArray.Close() }) // Bump the value by 1 using a bpf program. want := uint32(1338) a32, err := VariablePointer[atomic.Uint32](obj.Atomic) qt.Assert(t, qt.IsNil(err)) a32.Store(want - 1) mustReturn(t, obj.AddAtomic, 0) qt.Assert(t, qt.Equals(a32.Load(), want)) _, err = VariablePointer[*uint32](obj.Atomic) qt.Assert(t, qt.ErrorIs(err, ErrInvalidType)) _, err = VariablePointer[struct{ _ *uint64 }](obj.StructPad) qt.Assert(t, qt.ErrorIs(err, ErrInvalidType)) type S struct { _ structs.HostLayout A uint32 B uint64 C uint16 D [5]byte E uint64 } s, err := VariablePointer[S](obj.StructPad) qt.Assert(t, qt.IsNil(err)) *s = S{A: 0xa, B: 0xb, C: 0xc, D: [5]byte{0xd, 0, 0, 0, 0}, E: 0xe} mustReturn(t, obj.CheckStructPad, 1) a, err := VariablePointer[[8192]byte](obj.Array) qt.Assert(t, qt.IsNil(err)) a[len(a)-1] = 0xff mustReturn(t, obj.CheckArray, 1) } func TestVariablePointerError(t *testing.T) { testutils.SkipIfNotSupported(t, haveMmapableMaps()) file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) obj := struct { Atomic *Variable `ebpf:"var_atomic"` }{} qt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil))) _, err = VariablePointer[atomic.Uint32](obj.Atomic) qt.Assert(t, qt.ErrorIs(err, ErrNotSupported)) } func TestVariablePointerGC(t *testing.T) { testutils.SkipIfNotSupported(t, haveMmapableMaps()) file := testutils.NativeFile(t, "testdata/variables-%s.elf") spec, err := LoadCollectionSpec(file) qt.Assert(t, qt.IsNil(err)) cancel := make(chan struct{}) type obj_s struct { AddAtomic *Program `ebpf:"add_atomic"` Atomic *Variable `ebpf:"var_atomic"` AtomicMap *Map `ebpf:".data.atomic"` } unsafeMemory = true t.Cleanup(func() { unsafeMemory = false }) var obj obj_s qt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil))) // Set cleanup on obj to get notified when it is collected. ogc := make(chan struct{}) runtime.AddCleanup(&obj, func(*byte) { close(ogc) }, nil) mem, err := obj.AtomicMap.unsafeMemory() qt.Assert(t, qt.IsNil(err)) obj.AtomicMap.Close() // Start a goroutine that panics if the finalizer runs before we expect it to. mgc := make(chan struct{}) go func() { select { case <-mgc: panic("memory cleanup ran unexpectedly") case <-cancel: return } }() // Set cleanup on the Memory's backing array to get notified when it is // collected. runtime.AddCleanup(unsafe.SliceData(mem.b), func(*byte) { close(mgc) }, nil) // Pull out Program handle and Variable pointer so reference to obj is // dropped. prog := obj.AddAtomic t.Cleanup(func() { prog.Close() }) a32, err := VariablePointer[atomic.Uint32](obj.Atomic) qt.Assert(t, qt.IsNil(err)) // No references to obj past this point. Trigger GC and wait for the obj // finalizer to complete. runtime.GC() testutils.WaitChan(t, ogc, time.Second) // Trigger prog and read memory to ensure variable reference is still valid. mustReturn(t, prog, 0) qt.Assert(t, qt.Equals(a32.Load(), 1)) // Close the cancel channel while holding a backing array reference to avoid // false-positive panics in case we get a GC cycle before the manual call to // runtime.GC below. close(cancel) runtime.KeepAlive(a32) // More GC cycles to collect the backing array. As long as the unsafe memory // implementation is still on SetFinalizer, this needs multiple cycles to // work, since finalizers can resurrect objects. 3 GCs seems to work reliably. runtime.GC() runtime.GC() runtime.GC() // Wait for backing array to be finalized. testutils.WaitChan(t, mgc, time.Second*5) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������