mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-03 18:24:27 +08:00
Deploying to gh-pages from @ eunomia-bpf/bpf-developer-tutorial@ab0d1eef08 🚀
This commit is contained in:
1
.mdbookignore
Normal file
1
.mdbookignore
Normal file
@@ -0,0 +1 @@
|
||||
third_party
|
||||
10
43-kfuncs/.gitignore
vendored
Normal file
10
43-kfuncs/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.vscode
|
||||
package.json
|
||||
*.o
|
||||
*.skel.json
|
||||
*.skel.yaml
|
||||
package.yaml
|
||||
ecli
|
||||
ecc
|
||||
.output
|
||||
kfunc
|
||||
141
43-kfuncs/Makefile
Normal file
141
43-kfuncs/Makefile
Normal file
@@ -0,0 +1,141 @@
|
||||
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
OUTPUT := .output
|
||||
CLANG ?= clang
|
||||
LIBBPF_SRC := $(abspath ../third_party/libbpf/src)
|
||||
BPFTOOL_SRC := $(abspath ../third_party/bpftool/src)
|
||||
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
|
||||
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
|
||||
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
|
||||
LIBBLAZESYM_SRC := $(abspath ../third_party/blazesym/)
|
||||
LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a)
|
||||
LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h)
|
||||
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
|
||||
| sed 's/arm.*/arm/' \
|
||||
| sed 's/aarch64/arm64/' \
|
||||
| sed 's/ppc64le/powerpc/' \
|
||||
| sed 's/mips.*/mips/' \
|
||||
| sed 's/riscv64/riscv/' \
|
||||
| sed 's/loongarch64/loongarch/')
|
||||
VMLINUX := ../third_party/vmlinux/$(ARCH)/vmlinux.h
|
||||
# Use our own libbpf API headers and Linux UAPI headers distributed with
|
||||
# libbpf to avoid dependency on system-wide headers, which could be missing or
|
||||
# outdated
|
||||
INCLUDES := -I$(OUTPUT) -I../third_party/libbpf/include/uapi -I$(dir $(VMLINUX))
|
||||
CFLAGS := -g -Wall
|
||||
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
|
||||
|
||||
APPS = kfunc # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall
|
||||
|
||||
CARGO ?= $(shell which cargo)
|
||||
ifeq ($(strip $(CARGO)),)
|
||||
BZS_APPS :=
|
||||
else
|
||||
BZS_APPS := # profile
|
||||
APPS += $(BZS_APPS)
|
||||
# Required by libblazesym
|
||||
ALL_LDFLAGS += -lrt -ldl -lpthread -lm
|
||||
endif
|
||||
|
||||
# Get Clang's default includes on this system. We'll explicitly add these dirs
|
||||
# to the includes list when compiling with `-target bpf` because otherwise some
|
||||
# architecture-specific dirs will be "missing" on some architectures/distros -
|
||||
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
|
||||
# sys/cdefs.h etc. might be missing.
|
||||
#
|
||||
# Use '-idirafter': Don't interfere with include mechanics except where the
|
||||
# build would have failed anyways.
|
||||
CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \
|
||||
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
msg =
|
||||
else
|
||||
Q = @
|
||||
msg = @printf ' %-8s %s%s\n' \
|
||||
"$(1)" \
|
||||
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
|
||||
"$(if $(3), $(3))";
|
||||
MAKEFLAGS += --no-print-directory
|
||||
endif
|
||||
|
||||
define allow-override
|
||||
$(if $(or $(findstring environment,$(origin $(1))),\
|
||||
$(findstring command line,$(origin $(1)))),,\
|
||||
$(eval $(1) = $(2)))
|
||||
endef
|
||||
|
||||
$(call allow-override,CC,$(CROSS_COMPILE)cc)
|
||||
$(call allow-override,LD,$(CROSS_COMPILE)ld)
|
||||
|
||||
.PHONY: all
|
||||
all: $(APPS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(call msg,CLEAN)
|
||||
$(Q)rm -rf $(OUTPUT) $(APPS)
|
||||
|
||||
$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
|
||||
$(call msg,MKDIR,$@)
|
||||
$(Q)mkdir -p $@
|
||||
|
||||
# Build libbpf
|
||||
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
|
||||
$(call msg,LIB,$@)
|
||||
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
|
||||
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
|
||||
INCLUDEDIR= LIBDIR= UAPIDIR= \
|
||||
install
|
||||
|
||||
# Build bpftool
|
||||
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
|
||||
$(call msg,BPFTOOL,$@)
|
||||
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap
|
||||
|
||||
|
||||
$(LIBBLAZESYM_SRC)/target/release/libblazesym.a::
|
||||
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release
|
||||
|
||||
$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
|
||||
$(call msg,LIB, $@)
|
||||
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@
|
||||
|
||||
$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
|
||||
$(call msg,LIB,$@)
|
||||
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@
|
||||
|
||||
# Build BPF code
|
||||
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL)
|
||||
$(call msg,BPF,$@)
|
||||
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
|
||||
$(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \
|
||||
-c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
|
||||
$(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
|
||||
|
||||
# Generate BPF skeletons
|
||||
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
|
||||
$(call msg,GEN-SKEL,$@)
|
||||
$(Q)$(BPFTOOL) gen skeleton $< > $@
|
||||
|
||||
# Build user-space code
|
||||
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
|
||||
|
||||
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
|
||||
$(call msg,CC,$@)
|
||||
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
|
||||
|
||||
$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER)
|
||||
|
||||
$(BZS_APPS): $(LIBBLAZESYM_OBJ)
|
||||
|
||||
# Build application binary
|
||||
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
|
||||
$(call msg,BINARY,$@)
|
||||
$(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@
|
||||
|
||||
# delete failed targets
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
# keep intermediate (.skel.h, .bpf.o, etc) targets
|
||||
.SECONDARY:
|
||||
22
43-kfuncs/kfunc.bpf.c
Normal file
22
43-kfuncs/kfunc.bpf.c
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#define BPF_NO_GLOBAL_DATA
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef int pid_t;
|
||||
|
||||
extern u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d) __ksym;
|
||||
|
||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
|
||||
SEC("kprobe/do_unlinkat")
|
||||
int handle_kprobe(void *ctx)
|
||||
{
|
||||
pid_t pid = bpf_get_current_pid_tgid() >> 32;
|
||||
u64 result = bpf_kfunc_call_test(1, 2, 3, 4);
|
||||
bpf_printk("BPF triggered do_unlinkat from PID %d. Result: %lld\n", pid, result);
|
||||
return 0;
|
||||
}
|
||||
76
43-kfuncs/kfunc.c
Normal file
76
43-kfuncs/kfunc.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "kfunc.skel.h" // Include the generated skeleton header
|
||||
|
||||
static volatile bool exiting = false;
|
||||
|
||||
// Signal handler for graceful termination
|
||||
void handle_signal(int sig) {
|
||||
exiting = true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct kfunc_bpf *skel;
|
||||
int err;
|
||||
|
||||
// Handle SIGINT and SIGTERM for graceful shutdown
|
||||
signal(SIGINT, handle_signal);
|
||||
|
||||
// Open the BPF application
|
||||
skel = kfunc_bpf__open();
|
||||
if (!skel) {
|
||||
fprintf(stderr, "Failed to open BPF skeleton\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load & verify the BPF program
|
||||
err = kfunc_bpf__load(skel);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to load and verify BPF skeleton: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Attach the BPF program (e.g., attach kprobe)
|
||||
err = kfunc_bpf__attach(skel);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
printf("BPF program loaded and attached successfully. Press Ctrl-C to exit.\n");
|
||||
|
||||
// Optionally, read the trace_pipe to see bpf_printk outputs
|
||||
FILE *trace_pipe = fopen("/sys/kernel/debug/tracing/trace_pipe", "r");
|
||||
if (!trace_pipe) {
|
||||
perror("fopen trace_pipe");
|
||||
// Continue without reading trace_pipe
|
||||
}
|
||||
|
||||
// Main loop
|
||||
while (!exiting) {
|
||||
if (trace_pipe) {
|
||||
char buffer[256];
|
||||
if (fgets(buffer, sizeof(buffer), trace_pipe)) {
|
||||
printf("%s", buffer);
|
||||
} else {
|
||||
if (errno == EINTR)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// If trace_pipe is not available, just sleep
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_pipe)
|
||||
fclose(trace_pipe);
|
||||
|
||||
cleanup:
|
||||
// Clean up and destroy the BPF program
|
||||
kfunc_bpf__destroy(skel);
|
||||
return err < 0 ? -err : 0;
|
||||
}
|
||||
23
43-kfuncs/module/.gitignore
vendored
Normal file
23
43-kfuncs/module/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Ignore object files and kernel modules
|
||||
*.o
|
||||
*.ko
|
||||
*.mod
|
||||
*.mod.c
|
||||
*.symvers
|
||||
*.order
|
||||
|
||||
# Ignore temporary and backup files
|
||||
*~
|
||||
*.bak
|
||||
*.tmp
|
||||
*.swp
|
||||
|
||||
# Ignore build directory if generated
|
||||
/Module.symvers
|
||||
/Modules.markers
|
||||
/Module.markers
|
||||
/modules.order
|
||||
|
||||
# Ignore other automatically generated files
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
11
43-kfuncs/module/Makefile
Normal file
11
43-kfuncs/module/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
obj-m += hello.o # hello.o is the target
|
||||
|
||||
# Enable BTF generation
|
||||
KBUILD_CFLAGS += -g -O2
|
||||
|
||||
all:
|
||||
# Compile the module with BTF information
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
11
43-kfuncs/module/compact.h
Normal file
11
43-kfuncs/module/compact.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Compatible for lower kernel versions. No need in 6.11.
|
||||
#ifndef BTF_SET8_KFUNCS
|
||||
/* This flag implies BTF_SET8 holds kfunc(s) */
|
||||
#define BTF_SET8_KFUNCS (1 << 0)
|
||||
#endif
|
||||
#ifndef BTF_KFUNCS_START
|
||||
#define BTF_KFUNCS_START(name) static struct btf_id_set8 __maybe_unused name = { .flags = BTF_SET8_KFUNCS };
|
||||
#endif
|
||||
#ifndef BTF_KFUNCS_END
|
||||
#define BTF_KFUNCS_END(name)
|
||||
#endif
|
||||
61
43-kfuncs/module/hello.c
Normal file
61
43-kfuncs/module/hello.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <linux/init.h> // Macros for module initialization
|
||||
#include <linux/module.h> // Core header for loading modules
|
||||
#include <linux/kernel.h> // Kernel logging macros
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d);
|
||||
|
||||
/* Define a kfunc function */
|
||||
__bpf_kfunc_start_defs();
|
||||
|
||||
__bpf_kfunc u64 bpf_kfunc_call_test(u32 a, u64 b, u32 c, u64 d)
|
||||
{
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
__bpf_kfunc_end_defs();
|
||||
|
||||
BTF_KFUNCS_START(bpf_kfunc_example_ids_set)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test)
|
||||
BTF_KFUNCS_END(bpf_kfunc_example_ids_set)
|
||||
|
||||
// Register the kfunc ID set
|
||||
static const struct btf_kfunc_id_set bpf_kfunc_example_set = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &bpf_kfunc_example_ids_set,
|
||||
};
|
||||
|
||||
// Function executed when the module is loaded
|
||||
static int __init hello_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "Hello, world!\n");
|
||||
// Register the BTF kfunc ID set
|
||||
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set);
|
||||
if (ret) {
|
||||
pr_err("bpf_kfunc_example: Failed to register BTF kfunc ID set\n");
|
||||
return ret;
|
||||
}
|
||||
printk(KERN_INFO "bpf_kfunc_example: Module loaded successfully\n");
|
||||
return 0; // Return 0 if successful
|
||||
}
|
||||
|
||||
// Function executed when the module is removed
|
||||
static void __exit hello_exit(void)
|
||||
{
|
||||
// Unregister the BTF kfunc ID set
|
||||
// unregister_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_kfunc_example_set);
|
||||
printk(KERN_INFO "Goodbye, world!\n");
|
||||
}
|
||||
|
||||
// Macros to define the module’s init and exit points
|
||||
module_init(hello_init);
|
||||
module_exit(hello_exit);
|
||||
|
||||
MODULE_LICENSE("GPL"); // License type (GPL)
|
||||
MODULE_AUTHOR("Your Name"); // Module author
|
||||
MODULE_DESCRIPTION("A simple module"); // Module description
|
||||
MODULE_VERSION("1.0"); // Module version
|
||||
2
third_party/blazesym/.gitignore
vendored
Normal file
2
third_party/blazesym/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
1
third_party/blazesym/.rustfmt.toml
vendored
Normal file
1
third_party/blazesym/.rustfmt.toml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_lines_upper_bound = 2
|
||||
38
third_party/blazesym/Cargo.toml
vendored
Normal file
38
third_party/blazesym/Cargo.toml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
[package]
|
||||
name = "blazesym"
|
||||
description = "BlazeSym is a library that symbolizes addresses where symbol names, source file names, and line numbers can be acquired."
|
||||
version = "0.1.0"
|
||||
authors = ["Kui-Feng <thinker.li@gmail.com>"]
|
||||
license-file = "LICENSE"
|
||||
repository = "https://github.com/libbpf/blazesym"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "blazesym"
|
||||
crate-type = ["cdylib", "rlib", "staticlib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["dont-generate-test-files"]
|
||||
|
||||
[dependencies]
|
||||
nix = "0.24"
|
||||
regex = "1.6"
|
||||
crossbeam-channel = "0.5"
|
||||
libc = "0.2.137"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.68"
|
||||
cbindgen = {version = "0.24", optional = true}
|
||||
|
||||
[features]
|
||||
cheader = ["cbindgen"]
|
||||
# Enable this feature to opt out of the generation of test files. That may be
|
||||
# useful when certain utilities are not installed or when there is no intention
|
||||
# to run tests.
|
||||
dont-generate-test-files = []
|
||||
# Enable code paths requiring a nightly toolchain. This feature is only meant to
|
||||
# be used for testing and benchmarking purposes, not for the core library, which
|
||||
# is expected to work on stable.
|
||||
nightly = []
|
||||
29
third_party/blazesym/LICENSE
vendored
Normal file
29
third_party/blazesym/LICENSE
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022, Kuifeng Lee
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder 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.
|
||||
153
third_party/blazesym/build.rs
vendored
Normal file
153
third_party/blazesym/build.rs
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::OsString;
|
||||
use std::ops::Deref as _;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::Context as _;
|
||||
use anyhow::Result;
|
||||
|
||||
/// Format a command with the given list of arguments as a string.
|
||||
fn format_command<C, A, S>(command: C, args: A) -> String
|
||||
where
|
||||
C: AsRef<OsStr>,
|
||||
A: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
args.into_iter().fold(
|
||||
command.as_ref().to_string_lossy().into_owned(),
|
||||
|mut cmd, arg| {
|
||||
cmd += " ";
|
||||
cmd += arg.as_ref().to_string_lossy().deref();
|
||||
cmd
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Run a command with the provided arguments.
|
||||
fn run<C, A, S>(command: C, args: A) -> Result<()>
|
||||
where
|
||||
C: AsRef<OsStr>,
|
||||
A: IntoIterator<Item = S> + Clone,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
let instance = Command::new(command.as_ref())
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.args(args.clone())
|
||||
.output()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to run `{}`",
|
||||
format_command(command.as_ref(), args.clone())
|
||||
)
|
||||
})?;
|
||||
|
||||
if !instance.status.success() {
|
||||
let code = if let Some(code) = instance.status.code() {
|
||||
format!(" ({code})")
|
||||
} else {
|
||||
" (terminated by signal)".to_string()
|
||||
};
|
||||
|
||||
let stderr = String::from_utf8_lossy(&instance.stderr);
|
||||
let stderr = stderr.trim_end();
|
||||
let stderr = if !stderr.is_empty() {
|
||||
format!(": {stderr}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
bail!(
|
||||
"`{}` reported non-zero exit-status{code}{stderr}",
|
||||
format_command(command, args),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compile `src` into `dst` using `cc`.
|
||||
fn cc(src: &Path, dst: &str, options: &[&str]) {
|
||||
let dst = src.with_file_name(dst);
|
||||
println!("cargo:rerun-if-changed={}", src.display());
|
||||
println!("cargo:rerun-if-changed={}", dst.display());
|
||||
|
||||
// Ideally we'd use the `cc` crate here, but it seemingly can't be convinced
|
||||
// to create binaries.
|
||||
run(
|
||||
"cc",
|
||||
options
|
||||
.iter()
|
||||
.map(OsStr::new)
|
||||
.chain([src.as_os_str(), "-o".as_ref(), dst.as_os_str()]),
|
||||
)
|
||||
.expect("failed to run `cc`")
|
||||
}
|
||||
|
||||
/// Convert debug information contained in `src` into GSYM in `dst` using
|
||||
/// `llvm-gsymutil`.
|
||||
fn gsym(src: &Path, dst: &str) {
|
||||
let dst = src.with_file_name(dst);
|
||||
println!("cargo:rerun-if-changed={}", src.display());
|
||||
println!("cargo:rerun-if-changed={}", dst.display());
|
||||
|
||||
let gsymutil = env::var_os("LLVM_GSYMUTIL").unwrap_or_else(|| OsString::from("llvm-gsymutil"));
|
||||
|
||||
run(
|
||||
gsymutil,
|
||||
["--convert".as_ref(), src, "--out-file".as_ref(), &dst],
|
||||
)
|
||||
.expect("failed to run `llvm-gsymutil`")
|
||||
}
|
||||
|
||||
/// Build the various test binaries.
|
||||
fn build_test_bins(crate_root: &Path) {
|
||||
let src = crate_root.join("data").join("test.c");
|
||||
cc(&src, "test-no-debug.bin", &["-g0"]);
|
||||
cc(&src, "test-dwarf-v4.bin", &["-gdwarf-4"]);
|
||||
|
||||
let src = crate_root.join("data").join("test-gsym.c");
|
||||
let ld_script = crate_root.join("data").join("test-gsym.ld");
|
||||
let ld_script = ld_script.to_str().unwrap();
|
||||
println!("cargo:rerun-if-changed={ld_script}");
|
||||
cc(
|
||||
&src,
|
||||
"test-gsym.bin",
|
||||
&[
|
||||
"-gdwarf-4",
|
||||
"-T",
|
||||
ld_script,
|
||||
"-Wl,--build-id=none",
|
||||
"-O0",
|
||||
"-nostdlib",
|
||||
],
|
||||
);
|
||||
|
||||
let src = crate_root.join("data").join("test-gsym.bin");
|
||||
gsym(&src, "test.gsym");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let crate_dir = env!("CARGO_MANIFEST_DIR");
|
||||
|
||||
if !cfg!(feature = "dont-generate-test-files") {
|
||||
build_test_bins(crate_dir.as_ref());
|
||||
}
|
||||
|
||||
#[cfg(feature = "cheader")]
|
||||
{
|
||||
let build_type = env::var("PROFILE").unwrap();
|
||||
let target_path = Path::new(&crate_dir).join("target").join(build_type);
|
||||
|
||||
cbindgen::Builder::new()
|
||||
.with_crate(crate_dir)
|
||||
.with_config(cbindgen::Config::from_root_or_default(crate_dir))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings")
|
||||
.write_to_file(target_path.join("blazesym.h"));
|
||||
}
|
||||
}
|
||||
22
third_party/blazesym/cbindgen.toml
vendored
Normal file
22
third_party/blazesym/cbindgen.toml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
language = "C"
|
||||
include_guard = "__blazesym_h_"
|
||||
|
||||
[export]
|
||||
item_types = ["globals", "enums", "structs", "unions", "typedefs", "opaque", "functions"]
|
||||
|
||||
[fn]
|
||||
args = "Vertical"
|
||||
rename_args = "GeckoCase"
|
||||
|
||||
[struct]
|
||||
associated_constants_in_body = true
|
||||
derive_eq = true
|
||||
derive_ostream = true
|
||||
|
||||
[enum]
|
||||
add_sentinel = false
|
||||
derive_helper_methods = true
|
||||
derive_ostream = true
|
||||
|
||||
[macro_expansion]
|
||||
bitflags = true
|
||||
BIN
third_party/blazesym/data/dwarf-example
vendored
Executable file
BIN
third_party/blazesym/data/dwarf-example
vendored
Executable file
Binary file not shown.
22
third_party/blazesym/data/test-gsym.c
vendored
Normal file
22
third_party/blazesym/data/test-gsym.c
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/* The sample program is used to generate test.gsym.
|
||||
*
|
||||
* Chosen functions are placed in dedicated sections to allow for control placement.
|
||||
*/
|
||||
|
||||
__attribute__((section(".text.factorial"))) unsigned int
|
||||
factorial(unsigned int n) {
|
||||
if (n == 0)
|
||||
return 1;
|
||||
return factorial(n - 1) * n;
|
||||
}
|
||||
|
||||
static inline void
|
||||
factorial_inline_wrapper() {
|
||||
factorial(5);
|
||||
}
|
||||
|
||||
__attribute__((section(".text.main"))) int
|
||||
main(int argc, const char *argv[]) {
|
||||
factorial_inline_wrapper();
|
||||
return 0;
|
||||
}
|
||||
40
third_party/blazesym/data/test-gsym.ld
vendored
Normal file
40
third_party/blazesym/data/test-gsym.ld
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
SECTIONS {
|
||||
.text (0x2000000) : {
|
||||
*(.text.main)
|
||||
*(.text)
|
||||
. = ABSOLUTE(0x2000100);
|
||||
*(.text.factorial)
|
||||
}
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0.
|
||||
*/
|
||||
/* DWARF 1. */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions. */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2. */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2. */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.*)
|
||||
}
|
||||
}
|
||||
22
third_party/blazesym/data/test.c
vendored
Normal file
22
third_party/blazesym/data/test.c
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* The sample program is used to generate test.bin.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static
|
||||
unsigned int fibonacci(unsigned int n) {
|
||||
if (n <= 1)
|
||||
return n;
|
||||
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||
}
|
||||
|
||||
int
|
||||
main() {
|
||||
int i;
|
||||
printf("calculate fibonacci(n); n = ");
|
||||
scanf("%d", &i);
|
||||
|
||||
printf("fibonacci(%d) = %d\n", i, fibonacci(i));
|
||||
return 0;
|
||||
}
|
||||
44
third_party/blazesym/examples/addr2ln.rs
vendored
Normal file
44
third_party/blazesym/examples/addr2ln.rs
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
extern crate blazesym;
|
||||
|
||||
use blazesym::{BlazeSymbolizer, SymbolSrcCfg};
|
||||
use std::env;
|
||||
use std::path;
|
||||
|
||||
fn show_usage() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
println!("Usage: {} <file> <address>", args[0]);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() != 3 {
|
||||
show_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
let bin_name = &args[1];
|
||||
let mut addr_str = &args[2][..];
|
||||
let sym_srcs = [SymbolSrcCfg::Elf {
|
||||
file_name: path::PathBuf::from(bin_name),
|
||||
base_address: 0x0,
|
||||
}];
|
||||
let resolver = BlazeSymbolizer::new().unwrap();
|
||||
|
||||
if &addr_str[0..2] == "0x" {
|
||||
// Remove prefixed 0x
|
||||
addr_str = &addr_str[2..];
|
||||
}
|
||||
let addr = u64::from_str_radix(addr_str, 16).unwrap();
|
||||
|
||||
let results = resolver.symbolize(&sym_srcs, &[addr]);
|
||||
if results.len() == 1 && !results[0].is_empty() {
|
||||
let result = &results[0][0];
|
||||
println!(
|
||||
"0x{:x} @ {} {}:{}",
|
||||
addr, result.symbol, result.path, result.line_no
|
||||
);
|
||||
} else {
|
||||
println!("0x{addr:x} is not found");
|
||||
}
|
||||
}
|
||||
54
third_party/blazesym/examples/addr2ln_pid.rs
vendored
Normal file
54
third_party/blazesym/examples/addr2ln_pid.rs
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
extern crate blazesym;
|
||||
|
||||
use blazesym::{BlazeSymbolizer, SymbolSrcCfg, SymbolizedResult};
|
||||
use std::env;
|
||||
|
||||
fn show_usage() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
println!("Usage: {} <pid> <address>", args[0]);
|
||||
println!("Resolve an address in the process of the given pid, and");
|
||||
println!("print its symbol, the file name of the source, and the line number.");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() != 3 {
|
||||
show_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
let pid = args[1].parse::<u32>().unwrap();
|
||||
let mut addr_str = &args[2][..];
|
||||
println!("PID: {pid}");
|
||||
|
||||
if addr_str.len() > 2 && &addr_str[0..2] == "0x" {
|
||||
// Remove prefixed 0x
|
||||
addr_str = &addr_str[2..];
|
||||
}
|
||||
let addr = u64::from_str_radix(addr_str, 16).unwrap();
|
||||
|
||||
let sym_files = [SymbolSrcCfg::Process { pid: Some(pid) }];
|
||||
let resolver = BlazeSymbolizer::new().unwrap();
|
||||
let symlist = resolver.symbolize(&sym_files, &[addr]);
|
||||
if !symlist[0].is_empty() {
|
||||
let SymbolizedResult {
|
||||
symbol,
|
||||
start_address,
|
||||
path,
|
||||
line_no,
|
||||
column: _,
|
||||
} = &symlist[0][0];
|
||||
println!(
|
||||
"0x{:x} {}@0x{:x}+{} {}:{}",
|
||||
addr,
|
||||
symbol,
|
||||
start_address,
|
||||
addr - start_address,
|
||||
path,
|
||||
line_no
|
||||
);
|
||||
} else {
|
||||
println!("0x{addr:x} is not found");
|
||||
}
|
||||
}
|
||||
196
third_party/blazesym/scripts/gen-one-page.py
vendored
Normal file
196
third_party/blazesym/scripts/gen-one-page.py
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
'''Generate one page API document from rustdoc.
|
||||
|
||||
This script parses HTML files generated by rustoc and generates a single page API
|
||||
document.
|
||||
|
||||
'''
|
||||
from html.parser import HTMLParser
|
||||
|
||||
pages = ['index.html',
|
||||
'struct.BlazeSymbolizer.html',
|
||||
'struct.SymbolizedResult.html',
|
||||
'enum.SymbolSrcCfg.html',
|
||||
'enum.SymbolizerFeature.html',
|
||||
'fn.blazesym_new.html',
|
||||
'fn.blazesym_free.html',
|
||||
'fn.blazesym_symbolize.html',
|
||||
'fn.blazesym_result_free.html',
|
||||
'struct.blazesym.html',
|
||||
'struct.sym_src_cfg.html',
|
||||
'enum.blazesym_src_type.html',
|
||||
'union.ssc_params.html',
|
||||
'struct.ssc_elf.html',
|
||||
'struct.ssc_kernel.html',
|
||||
'struct.ssc_process.html',
|
||||
'struct.blazesym_result.html',
|
||||
'struct.blazesym_entry.html',
|
||||
'struct.blazesym_csym.html',
|
||||
]
|
||||
|
||||
def replace_text(src, replace, start, stop):
|
||||
lines = src.split('\n')
|
||||
if start[0] == stop[0]:
|
||||
lineno = start[0]
|
||||
lines[lineno] = lines[lineno][:start[1]] + replace + lines[lineno][stop[1]:]
|
||||
else:
|
||||
lines[start[0]] = lines[start[0]][:start[1]]
|
||||
lines[stop[0]] = lines[stop[0]][stop[1]:]
|
||||
lines[start[0] + 1: stop[0]] = [replace]
|
||||
pass
|
||||
return '\n'.join(lines)
|
||||
|
||||
class MyParser(HTMLParser):
|
||||
def get_doc_range(self, start, end):
|
||||
lines = self.raw_data.split('\n')
|
||||
lines = lines[start[0] - 1: end[0]]
|
||||
lines[-1] = lines[-1][:end[1]]
|
||||
lines[0] = lines[0][start[1]:]
|
||||
content = '\n'.join(lines)
|
||||
if hasattr(self, 'replaces'):
|
||||
for replace, rstart, rstop in reversed(self.replaces):
|
||||
rstart = list(rstart)
|
||||
rstop = list(rstop)
|
||||
rstart[0] -= start[0]
|
||||
rstop[0] -= start[0]
|
||||
if rstart[0] == 0:
|
||||
rstart[1] -= start[1]
|
||||
pass
|
||||
if rstop[0] == 0:
|
||||
rstop[1] -= start[1]
|
||||
pass
|
||||
content = replace_text(content, replace, rstart, rstop)
|
||||
pass
|
||||
pass
|
||||
return content
|
||||
|
||||
def get_pos_end_tag(self):
|
||||
pos = self.getpos()
|
||||
lines = self.raw_data.split('\n')
|
||||
line = lines[pos[0] - 1]
|
||||
col = line.find('>', pos[1]) + 1
|
||||
return (pos[0], col)
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
attrs = dict(attrs)
|
||||
if tag == 'body':
|
||||
self.doc_type = attrs['class'].split()[-1]
|
||||
elif tag == 'section' and 'id' in attrs and attrs['id'] == 'main-content':
|
||||
if self.doc_type == 'crate':
|
||||
self.start_pos = self.getpos()
|
||||
elif self.doc_type in ('struct', 'enum', 'union'):
|
||||
if not hasattr(self, 'start_pos'):
|
||||
self.start_pos = self.getpos()
|
||||
pass
|
||||
pass
|
||||
elif self.doc_type == 'fn':
|
||||
if not hasattr(self, 'start_pos'):
|
||||
self.start_pos = self.getpos()
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
elif 'class' in attrs and 'example-wrap' in attrs['class'].split():
|
||||
self.replacing = ' ......<<EXAMPLE>>......<br/>'
|
||||
self.replace_depth = 0
|
||||
self.replace_start = self.getpos();
|
||||
elif 'class' in attrs and 'item-decl' in attrs['class'].split():
|
||||
self.replacing = ' ......<<DECLARATION>>......<br/>'
|
||||
self.replace_depth = 0
|
||||
self.replace_start = self.getpos()
|
||||
elif hasattr(self, 'replace_depth'):
|
||||
self.replace_depth += 1
|
||||
elif hasattr(self, 'depth'):
|
||||
self.depth += 1
|
||||
pass
|
||||
elif hasattr(self, 'start_pos'):
|
||||
if self.doc_type in ('struct', 'enum', 'union'):
|
||||
if 'id' in attrs and attrs['id'].endswith('implementations'):
|
||||
print('%s\n</section>' % self.get_doc_range(self.start_pos, self.getpos()))
|
||||
self.doc_type = ''
|
||||
pass
|
||||
elif self.doc_type == 'fn' and not hasattr(self, 'depth'):
|
||||
self.depth = 0
|
||||
pass
|
||||
elif self.doc_type == 'crate' and 'class' in attrs and attrs['class'].endswith('top-doc'):
|
||||
self.depth = 0
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if hasattr(self, 'replace_depth'):
|
||||
if self.replace_depth > 0:
|
||||
self.replace_depth -= 1
|
||||
else:
|
||||
del self.replace_depth
|
||||
if not hasattr(self, 'replaces'):
|
||||
self.replaces = []
|
||||
pass
|
||||
self.replaces.append((self.replacing, self.replace_start, self.get_pos_end_tag()))
|
||||
pass
|
||||
elif hasattr(self, 'depth'):
|
||||
if self.depth > 0:
|
||||
self.depth -= 1
|
||||
pass
|
||||
else:
|
||||
del self.depth
|
||||
if self.doc_type == 'crate':
|
||||
content = self.get_doc_range(self.start_pos, self.get_pos_end_tag())
|
||||
print('%s\n</section>' % content)
|
||||
pass
|
||||
elif self.doc_type == 'fn':
|
||||
print('%s\n</section>' % self.get_doc_range(self.start_pos, self.get_pos_end_tag()))
|
||||
self.doc_type = ''
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
print('<html>')
|
||||
print('<head>')
|
||||
print('<link rel="preload" as="font" type="font/woff2" crossorigin href="../SourceSerif4-Regular.ttf.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../FiraSans-Regular.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../FiraSans-Medium.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../SourceCodePro-Regular.ttf.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../SourceSerif4-Bold.ttf.woff2"><link rel="preload" as="font" type="font/woff2" crossorigin href="../SourceCodePro-Semibold.ttf.woff2">')
|
||||
print('<link rel="stylesheet" type="text/css" href="../normalize.css"><link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle"><link rel="stylesheet" type="text/css" href="../ayu.css" disabled><link rel="stylesheet" type="text/css" href="../dark.css" disabled><link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">')
|
||||
print('<link rel="stylesheet" href="../noscript.css">')
|
||||
print('''<style>
|
||||
.tooltip.ignore {
|
||||
visibility: hidden;
|
||||
}
|
||||
.out-of-band {
|
||||
visibility: hidden;
|
||||
}
|
||||
.example-wrap {
|
||||
display: block;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
.example-wrap::before {
|
||||
content: '......<<EXAMPLE>>......';
|
||||
}
|
||||
.item-decl {
|
||||
display: block;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
.item-decl::before {
|
||||
content: '......<<CODE BLOCK>>......';
|
||||
}
|
||||
</style>
|
||||
''')
|
||||
print('</head>')
|
||||
print('<body>')
|
||||
|
||||
for page in pages:
|
||||
print('<page filename="%s">' % page)
|
||||
p = MyParser()
|
||||
fo = open('blazesym/' + page, 'r')
|
||||
data = fo.read()
|
||||
p.raw_data = data
|
||||
p.feed(data)
|
||||
print('</page>')
|
||||
pass
|
||||
|
||||
print('</body>')
|
||||
print('</html>')
|
||||
948
third_party/blazesym/src/c_api.rs
vendored
Normal file
948
third_party/blazesym/src/c_api.rs
vendored
Normal file
@@ -0,0 +1,948 @@
|
||||
use std::alloc::{alloc, dealloc, Layout};
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::OsStr;
|
||||
use std::mem;
|
||||
use std::os::raw::c_char;
|
||||
use std::os::unix::ffi::OsStrExt as _;
|
||||
use std::path::PathBuf;
|
||||
use std::ptr;
|
||||
use std::u64;
|
||||
|
||||
use crate::BlazeSymbolizer;
|
||||
use crate::FindAddrFeature;
|
||||
use crate::SymbolInfo;
|
||||
use crate::SymbolSrcCfg;
|
||||
use crate::SymbolType;
|
||||
use crate::SymbolizedResult;
|
||||
use crate::SymbolizerFeature;
|
||||
|
||||
|
||||
/// Types of symbol sources and debug information for C API.
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
pub enum blazesym_src_type {
|
||||
/// Symbols and debug information from an ELF file.
|
||||
SRC_T_ELF,
|
||||
/// Symbols and debug information from a kernel image and its kallsyms.
|
||||
SRC_T_KERNEL,
|
||||
/// Symbols and debug information from a process, including loaded object files.
|
||||
SRC_T_PROCESS,
|
||||
}
|
||||
|
||||
/// The parameters to load symbols and debug information from an ELF.
|
||||
///
|
||||
/// Describes the path and address of an ELF file loaded in a
|
||||
/// process.
|
||||
#[repr(C)]
|
||||
pub struct ssc_elf {
|
||||
/// The file name of an ELF file.
|
||||
///
|
||||
/// It can be an executable or shared object.
|
||||
/// For example, passing "/bin/sh" will load symbols and debug information from `sh`.
|
||||
/// Whereas passing "/lib/libc.so.xxx" will load symbols and debug information from the libc.
|
||||
pub file_name: *const c_char,
|
||||
/// The base address is where the file's executable segment(s) is loaded.
|
||||
///
|
||||
/// It should be the address
|
||||
/// in the process mapping to the executable segment's first byte.
|
||||
/// For example, in /proc/<pid>/maps
|
||||
///
|
||||
/// ```text
|
||||
/// 7fe1b2dc4000-7fe1b2f80000 r-xp 00000000 00:1d 71695032 /usr/lib64/libc-2.28.so
|
||||
/// 7fe1b2f80000-7fe1b3180000 ---p 001bc000 00:1d 71695032 /usr/lib64/libc-2.28.so
|
||||
/// 7fe1b3180000-7fe1b3184000 r--p 001bc000 00:1d 71695032 /usr/lib64/libc-2.28.so
|
||||
/// 7fe1b3184000-7fe1b3186000 rw-p 001c0000 00:1d 71695032 /usr/lib64/libc-2.28.so
|
||||
/// ```
|
||||
///
|
||||
/// It reveals that the executable segment of libc-2.28.so was
|
||||
/// loaded at 0x7fe1b2dc4000. This base address is used to
|
||||
/// translate an address in the segment to the corresponding
|
||||
/// address in the ELF file.
|
||||
///
|
||||
/// A loader would load an executable segment with the permission of `x`
|
||||
/// (executable). For example, the first block is with the
|
||||
/// permission of `r-xp`.
|
||||
pub base_address: u64,
|
||||
}
|
||||
|
||||
/// The parameters to load symbols and debug information from a kernel.
|
||||
///
|
||||
/// Use a kernel image and a snapshot of its kallsyms as a source of symbols and
|
||||
/// debug information.
|
||||
#[repr(C)]
|
||||
pub struct ssc_kernel {
|
||||
/// The path of a copy of kallsyms.
|
||||
///
|
||||
/// It can be `"/proc/kallsyms"` for the running kernel on the
|
||||
/// device. However, you can make copies for later. In that situation,
|
||||
/// you should give the path of a copy.
|
||||
/// Passing a `NULL`, by default, will result in `"/proc/kallsyms"`.
|
||||
pub kallsyms: *const c_char,
|
||||
/// The path of a kernel image.
|
||||
///
|
||||
/// The path of a kernel image should be, for instance,
|
||||
/// `"/boot/vmlinux-xxxx"`. For a `NULL` value, it will locate the
|
||||
/// kernel image of the running kernel in `"/boot/"` or
|
||||
/// `"/usr/lib/debug/boot/"`.
|
||||
pub kernel_image: *const c_char,
|
||||
}
|
||||
|
||||
/// The parameters to load symbols and debug information from a process.
|
||||
///
|
||||
/// Load all ELF files in a process as the sources of symbols and debug
|
||||
/// information.
|
||||
#[repr(C)]
|
||||
pub struct ssc_process {
|
||||
/// It is the PID of a process to symbolize.
|
||||
///
|
||||
/// BlazeSym will parse `/proc/<pid>/maps` and load all the object
|
||||
/// files.
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
/// Parameters of a symbol source.
|
||||
#[repr(C)]
|
||||
pub union ssc_params {
|
||||
/// The variant for SRC_T_ELF
|
||||
pub elf: mem::ManuallyDrop<ssc_elf>,
|
||||
/// The variant for SRC_T_KERNEL
|
||||
pub kernel: mem::ManuallyDrop<ssc_kernel>,
|
||||
/// The variant for SRC_T_PROCESS
|
||||
pub process: mem::ManuallyDrop<ssc_process>,
|
||||
}
|
||||
|
||||
/// Description of a source of symbols and debug information for C API.
|
||||
#[repr(C)]
|
||||
pub struct sym_src_cfg {
|
||||
/// A type of symbol source.
|
||||
pub src_type: blazesym_src_type,
|
||||
pub params: ssc_params,
|
||||
}
|
||||
|
||||
/// Names of the BlazeSym features.
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
pub enum blazesym_feature_name {
|
||||
/// Enable or disable returning line numbers of addresses.
|
||||
///
|
||||
/// Users should set `blazesym_feature.params.enable` to enabe or
|
||||
/// disable the feature,
|
||||
LINE_NUMBER_INFO,
|
||||
/// Enable or disable loading symbols from DWARF.
|
||||
///
|
||||
/// Users should `blazesym_feature.params.enable` to enable or
|
||||
/// disable the feature. This feature is disabled by default.
|
||||
DEBUG_INFO_SYMBOLS,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub union blazesym_feature_params {
|
||||
enable: bool,
|
||||
}
|
||||
|
||||
/// Setting of the blazesym features.
|
||||
///
|
||||
/// Contain parameters to enable, disable, or customize a feature.
|
||||
#[repr(C)]
|
||||
pub struct blazesym_feature {
|
||||
pub feature: blazesym_feature_name,
|
||||
pub params: blazesym_feature_params,
|
||||
}
|
||||
|
||||
/// A placeholder symbolizer for C API.
|
||||
///
|
||||
/// It is returned by [`blazesym_new()`] and should be free by
|
||||
/// [`blazesym_free()`].
|
||||
#[repr(C)]
|
||||
pub struct blazesym {
|
||||
symbolizer: *mut BlazeSymbolizer,
|
||||
}
|
||||
|
||||
/// The result of symbolization of an address for C API.
|
||||
///
|
||||
/// A `blazesym_csym` is the information of a symbol found for an
|
||||
/// address. One address may result in several symbols.
|
||||
#[repr(C)]
|
||||
pub struct blazesym_csym {
|
||||
/// The symbol name is where the given address should belong to.
|
||||
pub symbol: *const c_char,
|
||||
/// The address (i.e.,the first byte) is where the symbol is located.
|
||||
///
|
||||
/// The address is already relocated to the address space of
|
||||
/// the process.
|
||||
pub start_address: u64,
|
||||
/// The path of the source code defines the symbol.
|
||||
pub path: *const c_char,
|
||||
/// The instruction of the address is in the line number of the source code.
|
||||
pub line_no: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
/// `blazesym_entry` is the output of symbolization for an address for C API.
|
||||
///
|
||||
/// Every address has an `blazesym_entry` in
|
||||
/// [`blazesym_result::entries`] to collect symbols found by BlazeSym.
|
||||
#[repr(C)]
|
||||
pub struct blazesym_entry {
|
||||
/// The number of symbols found for an address.
|
||||
pub size: usize,
|
||||
/// All symbols found.
|
||||
///
|
||||
/// `syms` is an array of blazesym_csym in the size `size`.
|
||||
pub syms: *const blazesym_csym,
|
||||
}
|
||||
|
||||
/// `blazesym_result` is the result of symbolization for C API.
|
||||
///
|
||||
/// The instances of blazesym_result are returned from
|
||||
/// [`blazesym_symbolize()`]. They should be free by calling
|
||||
/// [`blazesym_result_free()`].
|
||||
#[repr(C)]
|
||||
pub struct blazesym_result {
|
||||
/// The number of addresses being symbolized.
|
||||
pub size: usize,
|
||||
/// The entries for addresses.
|
||||
///
|
||||
/// Symbolization occurs based on the order of addresses.
|
||||
/// Therefore, every address must have an entry here on the same
|
||||
/// order.
|
||||
pub entries: [blazesym_entry; 0],
|
||||
}
|
||||
|
||||
/// Create a `PathBuf` from a pointer of C string
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// C string should be terminated with a null byte.
|
||||
///
|
||||
unsafe fn from_cstr(cstr: *const c_char) -> PathBuf {
|
||||
PathBuf::from(unsafe { CStr::from_ptr(cstr) }.to_str().unwrap())
|
||||
}
|
||||
|
||||
unsafe fn symbolsrccfg_to_rust(cfg: *const sym_src_cfg, cfg_len: u32) -> Option<Vec<SymbolSrcCfg>> {
|
||||
let mut cfg_rs = Vec::<SymbolSrcCfg>::with_capacity(cfg_len as usize);
|
||||
|
||||
for i in 0..cfg_len {
|
||||
let c = unsafe { cfg.offset(i as isize) };
|
||||
match unsafe { &(*c).src_type } {
|
||||
blazesym_src_type::SRC_T_ELF => {
|
||||
cfg_rs.push(SymbolSrcCfg::Elf {
|
||||
file_name: unsafe { from_cstr((*c).params.elf.file_name) },
|
||||
base_address: unsafe { (*c).params.elf.base_address },
|
||||
});
|
||||
}
|
||||
blazesym_src_type::SRC_T_KERNEL => {
|
||||
let kallsyms = unsafe { (*c).params.kernel.kallsyms };
|
||||
let kernel_image = unsafe { (*c).params.kernel.kernel_image };
|
||||
cfg_rs.push(SymbolSrcCfg::Kernel {
|
||||
kallsyms: if !kallsyms.is_null() {
|
||||
Some(unsafe { from_cstr(kallsyms) })
|
||||
} else {
|
||||
None
|
||||
},
|
||||
kernel_image: if !kernel_image.is_null() {
|
||||
Some(unsafe { from_cstr(kernel_image) })
|
||||
} else {
|
||||
None
|
||||
},
|
||||
});
|
||||
}
|
||||
blazesym_src_type::SRC_T_PROCESS => {
|
||||
let pid = unsafe { (*c).params.process.pid };
|
||||
cfg_rs.push(SymbolSrcCfg::Process {
|
||||
pid: if pid > 0 { Some(pid) } else { None },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(cfg_rs)
|
||||
}
|
||||
|
||||
/// Create an instance of blazesym a symbolizer for C API.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Free the pointer with [`blazesym_free()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_new() -> *mut blazesym {
|
||||
let symbolizer = match BlazeSymbolizer::new() {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
};
|
||||
let symbolizer_box = Box::new(symbolizer);
|
||||
let c_box = Box::new(blazesym {
|
||||
symbolizer: Box::into_raw(symbolizer_box),
|
||||
});
|
||||
Box::into_raw(c_box)
|
||||
}
|
||||
|
||||
/// Create an instance of blazesym a symbolizer for C API.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Free the pointer with [`blazesym_free()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_new_opts(
|
||||
features: *const blazesym_feature,
|
||||
nfeatures: usize,
|
||||
) -> *mut blazesym {
|
||||
let features_v = unsafe {
|
||||
Vec::<blazesym_feature>::from_raw_parts(
|
||||
features as *mut blazesym_feature,
|
||||
nfeatures,
|
||||
nfeatures,
|
||||
)
|
||||
};
|
||||
let features_v = mem::ManuallyDrop::new(features_v);
|
||||
let features_r: Vec<_> = features_v
|
||||
.iter()
|
||||
.map(|x| -> SymbolizerFeature {
|
||||
match x.feature {
|
||||
blazesym_feature_name::LINE_NUMBER_INFO => {
|
||||
SymbolizerFeature::LineNumberInfo(unsafe { x.params.enable })
|
||||
}
|
||||
blazesym_feature_name::DEBUG_INFO_SYMBOLS => {
|
||||
SymbolizerFeature::DebugInfoSymbols(unsafe { x.params.enable })
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let symbolizer = match BlazeSymbolizer::new_opt(&features_r) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
};
|
||||
let symbolizer_box = Box::new(symbolizer);
|
||||
let c_box = Box::new(blazesym {
|
||||
symbolizer: Box::into_raw(symbolizer_box),
|
||||
});
|
||||
Box::into_raw(c_box)
|
||||
}
|
||||
|
||||
/// Free an instance of blazesym a symbolizer for C API.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The pointer must be returned by [`blazesym_new()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_free(symbolizer: *mut blazesym) {
|
||||
if !symbolizer.is_null() {
|
||||
drop(unsafe { Box::from_raw((*symbolizer).symbolizer) });
|
||||
drop(unsafe { Box::from_raw(symbolizer) });
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert SymbolizedResults to blazesym_results.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned pointer should be freed by [`blazesym_result_free()`].
|
||||
///
|
||||
unsafe fn convert_symbolizedresults_to_c(
|
||||
results: Vec<Vec<SymbolizedResult>>,
|
||||
) -> *const blazesym_result {
|
||||
// Allocate a buffer to contain a blazesym_result, all
|
||||
// blazesym_csym, and C strings of symbol and path.
|
||||
let strtab_size = results.iter().flatten().fold(0, |acc, result| {
|
||||
acc + result.symbol.len() + result.path.len() + 2
|
||||
});
|
||||
let all_csym_size = results.iter().flatten().count();
|
||||
let buf_size = strtab_size
|
||||
+ mem::size_of::<blazesym_result>()
|
||||
+ mem::size_of::<blazesym_entry>() * results.len()
|
||||
+ mem::size_of::<blazesym_csym>() * all_csym_size;
|
||||
let raw_buf_with_sz =
|
||||
unsafe { alloc(Layout::from_size_align(buf_size + mem::size_of::<u64>(), 8).unwrap()) };
|
||||
if raw_buf_with_sz.is_null() {
|
||||
return ptr::null();
|
||||
}
|
||||
|
||||
// prepend an u64 to keep the size of the buffer.
|
||||
unsafe { *(raw_buf_with_sz as *mut u64) = buf_size as u64 };
|
||||
|
||||
let raw_buf = unsafe { raw_buf_with_sz.add(mem::size_of::<u64>()) };
|
||||
|
||||
let result_ptr = raw_buf as *mut blazesym_result;
|
||||
let mut entry_last = unsafe { &mut (*result_ptr).entries as *mut blazesym_entry };
|
||||
let mut csym_last = unsafe {
|
||||
raw_buf.add(
|
||||
mem::size_of::<blazesym_result>() + mem::size_of::<blazesym_entry>() * results.len(),
|
||||
)
|
||||
} as *mut blazesym_csym;
|
||||
let mut cstr_last = unsafe {
|
||||
raw_buf.add(
|
||||
mem::size_of::<blazesym_result>()
|
||||
+ mem::size_of::<blazesym_entry>() * results.len()
|
||||
+ mem::size_of::<blazesym_csym>() * all_csym_size,
|
||||
)
|
||||
} as *mut c_char;
|
||||
|
||||
let mut make_cstr = |src: &str| {
|
||||
let cstr = cstr_last;
|
||||
unsafe { ptr::copy(src.as_ptr(), cstr as *mut u8, src.len()) };
|
||||
unsafe { *cstr.add(src.len()) = 0 };
|
||||
cstr_last = unsafe { cstr_last.add(src.len() + 1) };
|
||||
|
||||
cstr
|
||||
};
|
||||
|
||||
unsafe { (*result_ptr).size = results.len() };
|
||||
|
||||
// Convert all SymbolizedResults to blazesym_entrys and blazesym_csyms
|
||||
for entry in results {
|
||||
unsafe { (*entry_last).size = entry.len() };
|
||||
unsafe { (*entry_last).syms = csym_last };
|
||||
entry_last = unsafe { entry_last.add(1) };
|
||||
|
||||
for r in entry {
|
||||
let symbol_ptr = make_cstr(&r.symbol);
|
||||
|
||||
let path_ptr = make_cstr(&r.path);
|
||||
|
||||
let csym_ref = unsafe { &mut *csym_last };
|
||||
csym_ref.symbol = symbol_ptr;
|
||||
csym_ref.start_address = r.start_address;
|
||||
csym_ref.path = path_ptr;
|
||||
csym_ref.line_no = r.line_no;
|
||||
csym_ref.column = r.column;
|
||||
|
||||
csym_last = unsafe { csym_last.add(1) };
|
||||
}
|
||||
}
|
||||
|
||||
result_ptr
|
||||
}
|
||||
|
||||
/// Symbolize addresses with the sources of symbols and debug info.
|
||||
///
|
||||
/// Return an array of [`blazesym_result`] with the same size as the
|
||||
/// number of input addresses. The caller should free the returned
|
||||
/// array by calling [`blazesym_result_free()`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned pointer should be freed by [`blazesym_result_free()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_symbolize(
|
||||
symbolizer: *mut blazesym,
|
||||
sym_srcs: *const sym_src_cfg,
|
||||
sym_srcs_len: u32,
|
||||
addrs: *const u64,
|
||||
addr_cnt: usize,
|
||||
) -> *const blazesym_result {
|
||||
let sym_srcs_rs =
|
||||
if let Some(sym_srcs_rs) = unsafe { symbolsrccfg_to_rust(sym_srcs, sym_srcs_len) } {
|
||||
sym_srcs_rs
|
||||
} else {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("Fail to transform configurations of symbolizer from C to Rust");
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let symbolizer = unsafe { &*(*symbolizer).symbolizer };
|
||||
let addresses = unsafe { Vec::from_raw_parts(addrs as *mut u64, addr_cnt, addr_cnt) };
|
||||
|
||||
let results = symbolizer.symbolize(&sym_srcs_rs, &addresses);
|
||||
|
||||
addresses.leak();
|
||||
|
||||
if results.is_empty() {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("Empty result while request for {addr_cnt}");
|
||||
return ptr::null();
|
||||
}
|
||||
|
||||
unsafe { convert_symbolizedresults_to_c(results) }
|
||||
}
|
||||
|
||||
/// Free an array returned by blazesym_symbolize.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The pointer must be returned by [`blazesym_symbolize()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_result_free(results: *const blazesym_result) {
|
||||
if results.is_null() {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("blazesym_result_free(null)");
|
||||
return;
|
||||
}
|
||||
|
||||
let raw_buf_with_sz = unsafe { (results as *mut u8).offset(-(mem::size_of::<u64>() as isize)) };
|
||||
let sz = unsafe { *(raw_buf_with_sz as *mut u64) } as usize + mem::size_of::<u64>();
|
||||
unsafe { dealloc(raw_buf_with_sz, Layout::from_size_align(sz, 8).unwrap()) };
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct blazesym_sym_info {
|
||||
name: *const u8,
|
||||
address: u64,
|
||||
size: u64,
|
||||
sym_type: blazesym_sym_type,
|
||||
file_offset: u64,
|
||||
obj_file_name: *const u8,
|
||||
}
|
||||
|
||||
/// Convert SymbolInfos returned by BlazeSymbolizer::find_addresses() to a C array.
|
||||
unsafe fn convert_syms_list_to_c(
|
||||
syms_list: Vec<Vec<SymbolInfo>>,
|
||||
) -> *const *const blazesym_sym_info {
|
||||
let mut sym_cnt = 0;
|
||||
let mut str_buf_sz = 0;
|
||||
|
||||
for syms in &syms_list {
|
||||
sym_cnt += syms.len() + 1;
|
||||
for sym in syms {
|
||||
str_buf_sz += sym.name.len() + 1;
|
||||
if let Some(fname) = sym.obj_file_name.as_ref() {
|
||||
str_buf_sz += AsRef::<OsStr>::as_ref(fname).as_bytes().len() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let array_sz = ((mem::size_of::<*const u64>() * syms_list.len() + mem::size_of::<u64>() - 1)
|
||||
% mem::size_of::<u64>())
|
||||
* mem::size_of::<u64>();
|
||||
let sym_buf_sz = mem::size_of::<blazesym_sym_info>() * sym_cnt;
|
||||
let buf_size = array_sz + sym_buf_sz + str_buf_sz;
|
||||
let raw_buf_with_sz =
|
||||
unsafe { alloc(Layout::from_size_align(buf_size + mem::size_of::<u64>(), 8).unwrap()) };
|
||||
|
||||
unsafe { *(raw_buf_with_sz as *mut u64) = buf_size as u64 };
|
||||
|
||||
let raw_buf = unsafe { raw_buf_with_sz.add(mem::size_of::<u64>()) };
|
||||
let mut syms_ptr = raw_buf as *mut *mut blazesym_sym_info;
|
||||
let mut sym_ptr = unsafe { raw_buf.add(array_sz) } as *mut blazesym_sym_info;
|
||||
let mut str_ptr = unsafe { raw_buf.add(array_sz + sym_buf_sz) } as *mut u8;
|
||||
|
||||
for syms in syms_list {
|
||||
unsafe { *syms_ptr = sym_ptr };
|
||||
for SymbolInfo {
|
||||
name,
|
||||
address,
|
||||
size,
|
||||
sym_type,
|
||||
file_offset,
|
||||
obj_file_name,
|
||||
} in syms
|
||||
{
|
||||
let name_ptr = str_ptr as *const u8;
|
||||
unsafe { ptr::copy_nonoverlapping(name.as_ptr(), str_ptr, name.len()) };
|
||||
str_ptr = unsafe { str_ptr.add(name.len()) };
|
||||
unsafe { *str_ptr = 0 };
|
||||
str_ptr = unsafe { str_ptr.add(1) };
|
||||
let obj_file_name = if let Some(fname) = obj_file_name.as_ref() {
|
||||
let fname = AsRef::<OsStr>::as_ref(fname).as_bytes();
|
||||
let obj_fname_ptr = str_ptr;
|
||||
unsafe { ptr::copy_nonoverlapping(fname.as_ptr(), str_ptr, fname.len()) };
|
||||
str_ptr = unsafe { str_ptr.add(fname.len()) };
|
||||
unsafe { *str_ptr = 0 };
|
||||
str_ptr = unsafe { str_ptr.add(1) };
|
||||
obj_fname_ptr
|
||||
} else {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(*sym_ptr) = blazesym_sym_info {
|
||||
name: name_ptr,
|
||||
address,
|
||||
size,
|
||||
sym_type: match sym_type {
|
||||
SymbolType::Function => blazesym_sym_type::SYM_T_FUNC,
|
||||
SymbolType::Variable => blazesym_sym_type::SYM_T_VAR,
|
||||
_ => blazesym_sym_type::SYM_T_UNKNOWN,
|
||||
},
|
||||
file_offset,
|
||||
obj_file_name,
|
||||
}
|
||||
};
|
||||
sym_ptr = unsafe { sym_ptr.add(1) };
|
||||
}
|
||||
unsafe {
|
||||
(*sym_ptr) = blazesym_sym_info {
|
||||
name: ptr::null(),
|
||||
address: 0,
|
||||
size: 0,
|
||||
sym_type: blazesym_sym_type::SYM_T_UNKNOWN,
|
||||
file_offset: 0,
|
||||
obj_file_name: ptr::null(),
|
||||
}
|
||||
};
|
||||
sym_ptr = unsafe { sym_ptr.add(1) };
|
||||
|
||||
syms_ptr = unsafe { syms_ptr.add(1) };
|
||||
}
|
||||
|
||||
raw_buf as *const *const blazesym_sym_info
|
||||
}
|
||||
|
||||
/// Convert SymbolInfos returned by BlazeSymbolizer::find_address_regex() to a C array.
|
||||
unsafe fn convert_syms_to_c(syms: Vec<SymbolInfo>) -> *const blazesym_sym_info {
|
||||
let mut str_buf_sz = 0;
|
||||
|
||||
for sym in &syms {
|
||||
str_buf_sz += sym.name.len() + 1;
|
||||
if let Some(fname) = sym.obj_file_name.as_ref() {
|
||||
str_buf_sz += AsRef::<OsStr>::as_ref(fname).as_bytes().len() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
let sym_buf_sz = mem::size_of::<blazesym_sym_info>() * (syms.len() + 1);
|
||||
let buf_size = sym_buf_sz + str_buf_sz;
|
||||
let raw_buf_with_sz =
|
||||
unsafe { alloc(Layout::from_size_align(buf_size + mem::size_of::<u64>(), 8).unwrap()) };
|
||||
|
||||
unsafe { *(raw_buf_with_sz as *mut u64) = buf_size as u64 };
|
||||
|
||||
let raw_buf = unsafe { raw_buf_with_sz.add(mem::size_of::<u64>()) };
|
||||
let mut sym_ptr = raw_buf as *mut blazesym_sym_info;
|
||||
let mut str_ptr = unsafe { raw_buf.add(sym_buf_sz) } as *mut u8;
|
||||
|
||||
for sym in syms {
|
||||
let SymbolInfo {
|
||||
name,
|
||||
address,
|
||||
size,
|
||||
sym_type,
|
||||
file_offset,
|
||||
obj_file_name,
|
||||
} = sym;
|
||||
let name_ptr = str_ptr as *const u8;
|
||||
unsafe { ptr::copy_nonoverlapping(name.as_ptr(), str_ptr, name.len()) };
|
||||
str_ptr = unsafe { str_ptr.add(name.len()) };
|
||||
unsafe { *str_ptr = 0 };
|
||||
str_ptr = unsafe { str_ptr.add(1) };
|
||||
let obj_file_name = if let Some(fname) = obj_file_name.as_ref() {
|
||||
let fname = AsRef::<OsStr>::as_ref(fname).as_bytes();
|
||||
let obj_fname_ptr = str_ptr;
|
||||
unsafe { ptr::copy_nonoverlapping(fname.as_ptr(), str_ptr, fname.len()) };
|
||||
str_ptr = unsafe { str_ptr.add(fname.len()) };
|
||||
unsafe { *str_ptr = 0 };
|
||||
str_ptr = unsafe { str_ptr.add(1) };
|
||||
obj_fname_ptr
|
||||
} else {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(*sym_ptr) = blazesym_sym_info {
|
||||
name: name_ptr,
|
||||
address,
|
||||
size,
|
||||
sym_type: match sym_type {
|
||||
SymbolType::Function => blazesym_sym_type::SYM_T_FUNC,
|
||||
SymbolType::Variable => blazesym_sym_type::SYM_T_VAR,
|
||||
_ => blazesym_sym_type::SYM_T_UNKNOWN,
|
||||
},
|
||||
file_offset,
|
||||
obj_file_name,
|
||||
}
|
||||
};
|
||||
sym_ptr = unsafe { sym_ptr.add(1) };
|
||||
}
|
||||
unsafe {
|
||||
(*sym_ptr) = blazesym_sym_info {
|
||||
name: ptr::null(),
|
||||
address: 0,
|
||||
size: 0,
|
||||
sym_type: blazesym_sym_type::SYM_T_UNKNOWN,
|
||||
file_offset: 0,
|
||||
obj_file_name: ptr::null(),
|
||||
}
|
||||
};
|
||||
|
||||
raw_buf as *const blazesym_sym_info
|
||||
}
|
||||
|
||||
/// The types of symbols.
|
||||
///
|
||||
/// This type is used to choice what type of symbols you like to find
|
||||
/// and indicate the types of symbols found.
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum blazesym_sym_type {
|
||||
/// Invalid type
|
||||
SYM_T_INVALID,
|
||||
/// You want to find a symbol of any type.
|
||||
SYM_T_UNKNOWN,
|
||||
/// The returned symbol is a function, or you want to find a function.
|
||||
SYM_T_FUNC,
|
||||
/// The returned symbol is a variable, or you want to find a variable.
|
||||
SYM_T_VAR,
|
||||
}
|
||||
|
||||
/// Feature names of looking up addresses of symbols.
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
pub enum blazesym_faf_type {
|
||||
/// Invalid type
|
||||
FAF_T_INVALID,
|
||||
/// Return the offset in the file. (enable)
|
||||
FAF_T_OFFSET_IN_FILE,
|
||||
/// Return the file name of the shared object. (enable)
|
||||
FAF_T_OBJ_FILE_NAME,
|
||||
/// Return symbols having the given type. (sym_type)
|
||||
FAF_T_SYMBOL_TYPE,
|
||||
}
|
||||
|
||||
/// The parameter parts of `blazesym_faddr_feature`.
|
||||
#[repr(C)]
|
||||
pub union blazesym_faf_param {
|
||||
enable: bool,
|
||||
sym_type: blazesym_sym_type,
|
||||
}
|
||||
|
||||
/// Switches and settings of features of looking up addresses of
|
||||
/// symbols.
|
||||
///
|
||||
/// See [`FindAddrFeature`] for details.
|
||||
#[repr(C)]
|
||||
pub struct blazesym_faddr_feature {
|
||||
ftype: blazesym_faf_type,
|
||||
param: blazesym_faf_param,
|
||||
}
|
||||
|
||||
unsafe fn convert_find_addr_features(
|
||||
features: *const blazesym_faddr_feature,
|
||||
num_features: usize,
|
||||
) -> Vec<FindAddrFeature> {
|
||||
let mut feature = features;
|
||||
let mut features_ret = vec![];
|
||||
for _ in 0..num_features {
|
||||
match unsafe { &(*feature).ftype } {
|
||||
blazesym_faf_type::FAF_T_SYMBOL_TYPE => {
|
||||
features_ret.push(match unsafe { (*feature).param.sym_type } {
|
||||
blazesym_sym_type::SYM_T_UNKNOWN => {
|
||||
FindAddrFeature::SymbolType(SymbolType::Unknown)
|
||||
}
|
||||
blazesym_sym_type::SYM_T_FUNC => {
|
||||
FindAddrFeature::SymbolType(SymbolType::Function)
|
||||
}
|
||||
blazesym_sym_type::SYM_T_VAR => {
|
||||
FindAddrFeature::SymbolType(SymbolType::Variable)
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid symbol type");
|
||||
}
|
||||
});
|
||||
}
|
||||
blazesym_faf_type::FAF_T_OFFSET_IN_FILE => {
|
||||
features_ret.push(FindAddrFeature::OffsetInFile(unsafe {
|
||||
(*feature).param.enable
|
||||
}));
|
||||
}
|
||||
blazesym_faf_type::FAF_T_OBJ_FILE_NAME => {
|
||||
features_ret.push(FindAddrFeature::ObjFileName(unsafe {
|
||||
(*feature).param.enable
|
||||
}));
|
||||
}
|
||||
_ => {
|
||||
panic!("Unknown find_address feature type");
|
||||
}
|
||||
}
|
||||
feature = unsafe { feature.add(1) };
|
||||
}
|
||||
|
||||
features_ret
|
||||
}
|
||||
|
||||
/// Find the addresses of symbols matching a pattern.
|
||||
///
|
||||
/// Return an array of `blazesym_sym_info` ending with an item having a null address.
|
||||
/// input names. The caller should free the returned array by calling
|
||||
/// [`blazesym_syms_free()`].
|
||||
///
|
||||
/// It works the same as [`blazesym_find_address_regex()`] with
|
||||
/// additional controls on features.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned pointer should be free by [`blazesym_syms_free()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_find_address_regex_opt(
|
||||
symbolizer: *mut blazesym,
|
||||
sym_srcs: *const sym_src_cfg,
|
||||
sym_srcs_len: u32,
|
||||
pattern: *const c_char,
|
||||
features: *const blazesym_faddr_feature,
|
||||
num_features: usize,
|
||||
) -> *const blazesym_sym_info {
|
||||
let sym_srcs_rs =
|
||||
if let Some(sym_srcs_rs) = unsafe { symbolsrccfg_to_rust(sym_srcs, sym_srcs_len) } {
|
||||
sym_srcs_rs
|
||||
} else {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("Fail to transform configurations of symbolizer from C to Rust");
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let symbolizer = unsafe { &*(*symbolizer).symbolizer };
|
||||
|
||||
let pattern = unsafe { CStr::from_ptr(pattern) };
|
||||
let features = unsafe { convert_find_addr_features(features, num_features) };
|
||||
let syms =
|
||||
{ symbolizer.find_address_regex_opt(&sym_srcs_rs, pattern.to_str().unwrap(), features) };
|
||||
|
||||
if syms.is_none() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
unsafe { convert_syms_to_c(syms.unwrap()) }
|
||||
}
|
||||
|
||||
/// Find the addresses of symbols matching a pattern.
|
||||
///
|
||||
/// Return an array of `blazesym_sym_info` ending with an item having a null address.
|
||||
/// input names. The caller should free the returned array by calling
|
||||
/// [`blazesym_syms_free()`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned pointer should be free by [`blazesym_syms_free()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_find_address_regex(
|
||||
symbolizer: *mut blazesym,
|
||||
sym_srcs: *const sym_src_cfg,
|
||||
sym_srcs_len: u32,
|
||||
pattern: *const c_char,
|
||||
) -> *const blazesym_sym_info {
|
||||
unsafe {
|
||||
blazesym_find_address_regex_opt(symbolizer, sym_srcs, sym_srcs_len, pattern, ptr::null(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free an array returned by blazesym_find_addr_regex() or
|
||||
/// blazesym_find_addr_regex_opt().
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `syms` pointer should have been allocated by one of the
|
||||
/// `blazesym_find_address*` variants.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_syms_free(syms: *const blazesym_sym_info) {
|
||||
if syms.is_null() {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("blazesym_sym_info_free(null)");
|
||||
return;
|
||||
}
|
||||
|
||||
let raw_buf_with_sz = unsafe { (syms as *mut u8).offset(-(mem::size_of::<u64>() as isize)) };
|
||||
let sz = unsafe { *(raw_buf_with_sz as *mut u64) } as usize + mem::size_of::<u64>();
|
||||
unsafe { dealloc(raw_buf_with_sz, Layout::from_size_align(sz, 8).unwrap()) };
|
||||
}
|
||||
|
||||
/// Find the addresses of a list of symbols.
|
||||
///
|
||||
/// Return an array of `*const u64` with the same size as the
|
||||
/// input names. The caller should free the returned array by calling
|
||||
/// [`blazesym_syms_list_free()`].
|
||||
///
|
||||
/// Every name in the input name list may have more than one address.
|
||||
/// The respective entry in the returned array is an array containing
|
||||
/// all addresses and ended with a null (0x0).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned pointer should be free by [`blazesym_syms_list_free()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_find_addresses_opt(
|
||||
symbolizer: *mut blazesym,
|
||||
sym_srcs: *const sym_src_cfg,
|
||||
sym_srcs_len: u32,
|
||||
names: *const *const c_char,
|
||||
name_cnt: usize,
|
||||
features: *const blazesym_faddr_feature,
|
||||
num_features: usize,
|
||||
) -> *const *const blazesym_sym_info {
|
||||
let sym_srcs_rs =
|
||||
if let Some(sym_srcs_rs) = unsafe { symbolsrccfg_to_rust(sym_srcs, sym_srcs_len) } {
|
||||
sym_srcs_rs
|
||||
} else {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("Fail to transform configurations of symbolizer from C to Rust");
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let symbolizer = unsafe { &*(*symbolizer).symbolizer };
|
||||
|
||||
let mut names_cstr = vec![];
|
||||
for i in 0..name_cnt {
|
||||
let name_c = unsafe { *names.add(i) };
|
||||
let name_r = unsafe { CStr::from_ptr(name_c) };
|
||||
names_cstr.push(name_r);
|
||||
}
|
||||
let features = unsafe { convert_find_addr_features(features, num_features) };
|
||||
let syms = {
|
||||
let mut names_r = vec![];
|
||||
for name in names_cstr.iter().take(name_cnt) {
|
||||
names_r.push(name.to_str().unwrap());
|
||||
}
|
||||
symbolizer.find_addresses_opt(&sym_srcs_rs, &names_r, features)
|
||||
};
|
||||
|
||||
unsafe { convert_syms_list_to_c(syms) }
|
||||
}
|
||||
|
||||
/// Find addresses of a symbol name.
|
||||
///
|
||||
/// A symbol may have multiple addressses.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned data should be free by [`blazesym_syms_list_free()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_find_addresses(
|
||||
symbolizer: *mut blazesym,
|
||||
sym_srcs: *const sym_src_cfg,
|
||||
sym_srcs_len: u32,
|
||||
names: *const *const c_char,
|
||||
name_cnt: usize,
|
||||
) -> *const *const blazesym_sym_info {
|
||||
unsafe {
|
||||
blazesym_find_addresses_opt(
|
||||
symbolizer,
|
||||
sym_srcs,
|
||||
sym_srcs_len,
|
||||
names,
|
||||
name_cnt,
|
||||
ptr::null(),
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free an array returned by blazesym_find_addresses.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The pointer must be returned by [`blazesym_find_addresses()`].
|
||||
///
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn blazesym_syms_list_free(syms_list: *const *const blazesym_sym_info) {
|
||||
if syms_list.is_null() {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("blazesym_syms_list_free(null)");
|
||||
return;
|
||||
}
|
||||
|
||||
let raw_buf_with_sz =
|
||||
unsafe { (syms_list as *mut u8).offset(-(mem::size_of::<u64>() as isize)) };
|
||||
let sz = unsafe { *(raw_buf_with_sz as *mut u64) } as usize + mem::size_of::<u64>();
|
||||
unsafe { dealloc(raw_buf_with_sz, Layout::from_size_align(sz, 8).unwrap()) };
|
||||
}
|
||||
1489
third_party/blazesym/src/dwarf.rs
vendored
Normal file
1489
third_party/blazesym/src/dwarf.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
64
third_party/blazesym/src/dwarf/constants.rs
vendored
Normal file
64
third_party/blazesym/src/dwarf/constants.rs
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
pub const DW_UT_compile: u8 = 0x1;
|
||||
pub const DW_UT_type: u8 = 0x2;
|
||||
|
||||
pub const DW_TAG_array_type: u8 = 0x1;
|
||||
pub const DW_TAG_enumeration_type: u8 = 0x4;
|
||||
pub const DW_TAG_compile_unit: u8 = 0x11;
|
||||
pub const DW_TAG_subprogram: u8 = 0x2e;
|
||||
pub const DW_TAG_variable: u8 = 0x34;
|
||||
pub const DW_TAG_namespace: u8 = 0x39;
|
||||
|
||||
pub const DW_CHILDREN_no: u8 = 0x00;
|
||||
pub const DW_CHILDREN_yes: u8 = 0x01;
|
||||
|
||||
pub const DW_AT_sibling: u8 = 0x01;
|
||||
pub const DW_AT_location: u8 = 0x02;
|
||||
pub const DW_AT_name: u8 = 0x03;
|
||||
pub const DW_AT_lo_pc: u8 = 0x11;
|
||||
pub const DW_AT_hi_pc: u8 = 0x12;
|
||||
pub const DW_AT_entry_pc: u8 = 0x52;
|
||||
pub const DW_AT_linkage_name: u8 = 0x6e;
|
||||
|
||||
pub const DW_FORM_addr: u8 = 0x01;
|
||||
pub const DW_FORM_block2: u8 = 0x03;
|
||||
pub const DW_FORM_block4: u8 = 0x04;
|
||||
pub const DW_FORM_data2: u8 = 0x05;
|
||||
pub const DW_FORM_data4: u8 = 0x06;
|
||||
pub const DW_FORM_data8: u8 = 0x07;
|
||||
pub const DW_FORM_string: u8 = 0x08;
|
||||
pub const DW_FORM_block: u8 = 0x09;
|
||||
pub const DW_FORM_block1: u8 = 0x0a;
|
||||
pub const DW_FORM_data1: u8 = 0x0b;
|
||||
pub const DW_FORM_flag: u8 = 0x0c;
|
||||
pub const DW_FORM_sdata: u8 = 0x0d;
|
||||
pub const DW_FORM_strp: u8 = 0x0e;
|
||||
pub const DW_FORM_udata: u8 = 0x0f;
|
||||
pub const DW_FORM_ref_addr: u8 = 0x10;
|
||||
pub const DW_FORM_ref1: u8 = 0x11;
|
||||
pub const DW_FORM_ref2: u8 = 0x12;
|
||||
pub const DW_FORM_ref4: u8 = 0x13;
|
||||
pub const DW_FORM_ref8: u8 = 0x14;
|
||||
pub const DW_FORM_ref_udata: u8 = 0x15;
|
||||
pub const DW_FORM_indirect: u8 = 0x16;
|
||||
pub const DW_FORM_sec_offset: u8 = 0x17;
|
||||
pub const DW_FORM_exprloc: u8 = 0x18;
|
||||
pub const DW_FORM_flag_present: u8 = 0x19;
|
||||
pub const DW_FORM_strx: u8 = 0x1a;
|
||||
pub const DW_FORM_addrx: u8 = 0x1b;
|
||||
pub const DW_FORM_ref_sup4: u8 = 0x1c;
|
||||
pub const DW_FORM_strp_sup: u8 = 0x1d;
|
||||
pub const DW_FORM_data16: u8 = 0x1e;
|
||||
pub const DW_FORM_line_strp: u8 = 0x1f;
|
||||
pub const DW_FORM_ref_sig8: u8 = 0x20;
|
||||
pub const DW_FORM_implicit_const: u8 = 0x21;
|
||||
pub const DW_FORM_loclistx: u8 = 0x22;
|
||||
pub const DW_FORM_rnglistx: u8 = 0x23;
|
||||
pub const DW_FORM_ref_sup8: u8 = 0x24;
|
||||
pub const DW_FORM_str1: u8 = 0x25;
|
||||
pub const DW_FORM_str2: u8 = 0x26;
|
||||
pub const DW_FORM_str3: u8 = 0x27;
|
||||
pub const DW_FORM_str4: u8 = 0x28;
|
||||
pub const DW_FORM_addrx1: u8 = 0x29;
|
||||
pub const DW_FORM_addrx2: u8 = 0x2a;
|
||||
pub const DW_FORM_addrx3: u8 = 0x2b;
|
||||
pub const DW_FORM_addrx4: u8 = 0x2c;
|
||||
869
third_party/blazesym/src/dwarf/debug_info.rs
vendored
Normal file
869
third_party/blazesym/src/dwarf/debug_info.rs
vendored
Normal file
@@ -0,0 +1,869 @@
|
||||
//! Parse the `.debug_info` section to get Debug Information Entries.
|
||||
//!
|
||||
//! It supports DWARFv4 now. (See <https://dwarfstd.org/doc/DWARF4.pdf>)
|
||||
//! It parse DIEs from the `.debug_info` section and Abbreviations
|
||||
//! from the `.debg_abbrev` section.
|
||||
//!
|
||||
//! The `.debug_info` section is a list of (Compile-)Units. Every
|
||||
//! Unit comprises DIEs to carry debug information of a source file.
|
||||
//! A Unit starts with a header to describe the size of this unit in
|
||||
//! the section, the offset of its abbreviation table in the
|
||||
//! `.debug_abbrev` section, ..., and DWARF version. (version 4)
|
||||
//!
|
||||
//! A DIE starts with an index encoded in LEB128 to the abbreviation
|
||||
//! table of the Unit. The abbreviation given by the index describle
|
||||
//! the content, format and layout of a DIE. The abbreviation index
|
||||
//! starts from 1. 0 means a null entry. DIEs in an Unit are
|
||||
//! organized as a tree, parent-children. Null entries are used to
|
||||
//! signal the last sibling to end a level.
|
||||
//!
|
||||
//! A user starts a parser by creating an instance of [`UnitIter`].
|
||||
//! It will walk through the data in the `.debug_info` and
|
||||
//! `.debug_abbrev` section to return Units.
|
||||
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::iter::Iterator;
|
||||
use std::mem;
|
||||
|
||||
use crate::util::decode_leb128_128;
|
||||
use crate::util::decode_udword;
|
||||
use crate::util::decode_uhalf;
|
||||
use crate::util::decode_uword;
|
||||
use crate::util::ReadRaw as _;
|
||||
|
||||
use super::constants::*;
|
||||
|
||||
|
||||
fn read_3bytes(data: &mut &[u8]) -> Option<u32> {
|
||||
if let [b1, b2, b3] = data.read_slice(3)? {
|
||||
Some(*b1 as u32 | ((*b2 as u32) << 8) | ((*b3 as u32) << 16))
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct UnknownHeader {
|
||||
init_length: usize,
|
||||
bits64: bool,
|
||||
version: u16,
|
||||
unit_type: u8,
|
||||
hdr_size: usize,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct CUHeaderV5 {
|
||||
init_length: usize,
|
||||
bits64: bool,
|
||||
version: u16,
|
||||
unit_type: u8,
|
||||
address_size: u8,
|
||||
debug_abbrev_offset: u64,
|
||||
hdr_size: usize,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct CUHeaderV4 {
|
||||
init_length: usize,
|
||||
bits64: bool,
|
||||
version: u16,
|
||||
address_size: u8,
|
||||
debug_abbrev_offset: u64, // The location of the abbreviation table.
|
||||
hdr_size: usize,
|
||||
}
|
||||
|
||||
/// The Unit header.
|
||||
///
|
||||
/// With DWARFv4, an unit header describe a compile unit followed by
|
||||
/// DIEs of the unit in the `.debug_info` section. DWARFv5 is much
|
||||
/// more complicated.
|
||||
///
|
||||
/// So far, BlazeSym supports only DWARFv4, that is common used.
|
||||
pub enum UnitHeader {
|
||||
CompileV4(CUHeaderV4),
|
||||
CompileV5(CUHeaderV5),
|
||||
Unknown(UnknownHeader),
|
||||
}
|
||||
|
||||
impl UnitHeader {
|
||||
fn unit_size(&self) -> usize {
|
||||
match self {
|
||||
UnitHeader::CompileV4(h) => h.init_length + (if h.bits64 { 12 } else { 4 }),
|
||||
UnitHeader::CompileV5(h) => h.init_length + (if h.bits64 { 12 } else { 4 }),
|
||||
UnitHeader::Unknown(h) => h.init_length + (if h.bits64 { 12 } else { 4 }),
|
||||
}
|
||||
}
|
||||
|
||||
fn header_size(&self) -> usize {
|
||||
match self {
|
||||
UnitHeader::CompileV4(h) => h.hdr_size,
|
||||
UnitHeader::CompileV5(h) => h.hdr_size,
|
||||
UnitHeader::Unknown(h) => h.hdr_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AbbrevAttr {
|
||||
name: u8,
|
||||
form: u8,
|
||||
opt: u128,
|
||||
}
|
||||
|
||||
/// An abbreviation.
|
||||
///
|
||||
/// An abbrivation describes the format of a DIE. it comprises a list
|
||||
/// of specifications that describe the names and the formats of
|
||||
/// attributes. A DIE will be formated in the way described by it's
|
||||
/// abbreviation.
|
||||
pub struct Abbrev {
|
||||
/// The index to the abbreviation table.
|
||||
pub abbrev_code: u32,
|
||||
/// The type of the abbreviation.
|
||||
///
|
||||
/// It can be a DW_TAG_compile (a compile unit),
|
||||
/// DW_TAG_subprogram, DW_TAG_variable, ... etc.
|
||||
pub tag: u8,
|
||||
pub has_children: bool,
|
||||
|
||||
parsed_attrs: Vec<AbbrevAttr>,
|
||||
}
|
||||
|
||||
impl Abbrev {
|
||||
#[inline]
|
||||
pub fn all_attrs(&self) -> &[AbbrevAttr] {
|
||||
&self.parsed_attrs[..]
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an abbreviation from a buffer.
|
||||
///
|
||||
/// Include all attributes, names and forms.
|
||||
#[inline]
|
||||
fn parse_abbrev(data: &[u8]) -> Option<(Abbrev, usize)> {
|
||||
let (abbrev_code, bytes) = decode_leb128_128(data)?;
|
||||
if abbrev_code == 0 {
|
||||
return Some((
|
||||
Abbrev {
|
||||
abbrev_code: 0,
|
||||
tag: 0,
|
||||
has_children: false,
|
||||
parsed_attrs: vec![],
|
||||
},
|
||||
1,
|
||||
));
|
||||
}
|
||||
|
||||
let mut pos = bytes as usize;
|
||||
let (tag, bytes) = decode_leb128_128(&data[pos..])?;
|
||||
pos += bytes as usize;
|
||||
let has_children = data[pos] == DW_CHILDREN_yes;
|
||||
pos += 1;
|
||||
|
||||
let mut parsed_attrs = Vec::<AbbrevAttr>::new();
|
||||
while pos < data.len() {
|
||||
if let Some((name, form, opt, bytes)) = parse_abbrev_attr(&data[pos..]) {
|
||||
pos += bytes;
|
||||
parsed_attrs.push(AbbrevAttr { name, form, opt });
|
||||
if form == 0 {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Some((
|
||||
Abbrev {
|
||||
abbrev_code: abbrev_code as u32,
|
||||
tag: tag as u8,
|
||||
has_children,
|
||||
parsed_attrs,
|
||||
},
|
||||
pos,
|
||||
))
|
||||
}
|
||||
|
||||
/// Parse an attribute specification from a buffer.
|
||||
///
|
||||
/// Return the name, form, optional value and size of an abbreviation.
|
||||
#[inline]
|
||||
fn parse_abbrev_attr(data: &[u8]) -> Option<(u8, u8, u128, usize)> {
|
||||
let mut pos = 0; // Track the size of this abbreviation.
|
||||
let (name, bytes) = decode_leb128_128(&data[pos..])?;
|
||||
pos += bytes as usize;
|
||||
let (form, bytes) = decode_leb128_128(&data[pos..])?;
|
||||
pos += bytes as usize;
|
||||
let opt = if form as u8 == DW_FORM_implicit_const || form as u8 == DW_FORM_indirect {
|
||||
let (c, bytes) = decode_leb128_128(&data[pos..])?;
|
||||
pos += bytes as usize;
|
||||
c
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Some((name as u8, form as u8, opt, pos))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AttrValue<'a> {
|
||||
Signed128(i128),
|
||||
Unsigned(u64),
|
||||
Unsigned128(u128),
|
||||
Bytes(&'a [u8]),
|
||||
String(&'a str),
|
||||
}
|
||||
|
||||
fn extract_attr_value_impl<'data>(
|
||||
data: &mut &'data [u8],
|
||||
form: u8,
|
||||
dwarf_sz: usize,
|
||||
addr_sz: usize,
|
||||
) -> Option<AttrValue<'data>> {
|
||||
match form {
|
||||
DW_FORM_addr => {
|
||||
if addr_sz == 0x4 {
|
||||
Some(AttrValue::Unsigned(data.read_u32()? as u64))
|
||||
} else {
|
||||
Some(AttrValue::Unsigned(data.read_u64()?))
|
||||
}
|
||||
}
|
||||
DW_FORM_block2 => {
|
||||
let value = data.read_u16()?;
|
||||
Some(AttrValue::Bytes(data.read_slice(value.into())?))
|
||||
}
|
||||
DW_FORM_block4 => {
|
||||
let value = data.read_u32()?;
|
||||
Some(AttrValue::Bytes(data.read_slice(value as usize)?))
|
||||
}
|
||||
DW_FORM_data2 => Some(AttrValue::Unsigned(data.read_u16()?.into())),
|
||||
DW_FORM_data4 => Some(AttrValue::Unsigned(data.read_u32()?.into())),
|
||||
DW_FORM_data8 => Some(AttrValue::Unsigned(data.read_u64()?)),
|
||||
DW_FORM_string => {
|
||||
let string = data.read_cstr()?;
|
||||
Some(AttrValue::String(string.to_str().ok()?))
|
||||
}
|
||||
DW_FORM_block => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Bytes(data.read_slice(value as usize)?))
|
||||
}
|
||||
DW_FORM_block1 => {
|
||||
let value = data.read_u8()?;
|
||||
Some(AttrValue::Bytes(data.read_slice(value.into())?))
|
||||
}
|
||||
DW_FORM_data1 => Some(AttrValue::Unsigned(data.read_u8()?.into())),
|
||||
DW_FORM_flag => Some(AttrValue::Unsigned(data.read_u8()?.into())),
|
||||
DW_FORM_sdata => {
|
||||
let (value, _bytes) = data.read_i128_leb128()?;
|
||||
Some(AttrValue::Signed128(value))
|
||||
}
|
||||
DW_FORM_strp => {
|
||||
if dwarf_sz == 0x4 {
|
||||
Some(AttrValue::Unsigned(data.read_u32()?.into()))
|
||||
} else {
|
||||
Some(AttrValue::Unsigned(data.read_u64()?))
|
||||
}
|
||||
}
|
||||
DW_FORM_udata => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Unsigned128(value))
|
||||
}
|
||||
DW_FORM_ref_addr => {
|
||||
if dwarf_sz == 0x4 {
|
||||
Some(AttrValue::Unsigned(data.read_u32()?.into()))
|
||||
} else {
|
||||
Some(AttrValue::Unsigned(data.read_u64()?))
|
||||
}
|
||||
}
|
||||
DW_FORM_ref1 => Some(AttrValue::Unsigned(data.read_u8()?.into())),
|
||||
DW_FORM_ref2 => Some(AttrValue::Unsigned(data.read_u16()?.into())),
|
||||
DW_FORM_ref4 => Some(AttrValue::Unsigned(data.read_u32()?.into())),
|
||||
DW_FORM_ref8 => Some(AttrValue::Unsigned(data.read_u64()?)),
|
||||
DW_FORM_ref_udata => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Unsigned128(value))
|
||||
}
|
||||
DW_FORM_indirect => {
|
||||
let (f, _bytes) = data.read_u128_leb128()?;
|
||||
extract_attr_value_impl(data, f as u8, dwarf_sz, addr_sz)
|
||||
}
|
||||
DW_FORM_sec_offset => {
|
||||
if dwarf_sz == 0x4 {
|
||||
Some(AttrValue::Unsigned(data.read_u32()?.into()))
|
||||
} else {
|
||||
Some(AttrValue::Unsigned(data.read_u64()?))
|
||||
}
|
||||
}
|
||||
DW_FORM_exprloc => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Bytes(data.read_slice(value as usize)?))
|
||||
}
|
||||
DW_FORM_flag_present => Some(AttrValue::Unsigned(0)),
|
||||
DW_FORM_strx => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Unsigned(value as u64))
|
||||
}
|
||||
DW_FORM_addrx => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Unsigned(value as u64))
|
||||
}
|
||||
DW_FORM_ref_sup4 => Some(AttrValue::Unsigned(data.read_u32()?.into())),
|
||||
DW_FORM_strp_sup => {
|
||||
if dwarf_sz == 0x4 {
|
||||
Some(AttrValue::Unsigned(data.read_u32()?.into()))
|
||||
} else {
|
||||
Some(AttrValue::Unsigned(data.read_u64()?))
|
||||
}
|
||||
}
|
||||
DW_FORM_data16 => Some(AttrValue::Bytes(data.read_slice(16)?)),
|
||||
DW_FORM_line_strp => {
|
||||
if dwarf_sz == 0x4 {
|
||||
Some(AttrValue::Unsigned(data.read_u32()?.into()))
|
||||
} else {
|
||||
Some(AttrValue::Unsigned(data.read_u64()?))
|
||||
}
|
||||
}
|
||||
DW_FORM_ref_sig8 => Some(AttrValue::Bytes(data.read_slice(8)?)),
|
||||
DW_FORM_implicit_const => Some(AttrValue::Unsigned(0)),
|
||||
DW_FORM_loclistx => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Unsigned(value as u64))
|
||||
}
|
||||
DW_FORM_rnglistx => {
|
||||
let (value, _bytes) = data.read_u128_leb128()?;
|
||||
Some(AttrValue::Unsigned(value as u64))
|
||||
}
|
||||
DW_FORM_ref_sup8 => Some(AttrValue::Unsigned(data.read_u64()?)),
|
||||
DW_FORM_str1 => Some(AttrValue::Unsigned(data.read_u8()?.into())),
|
||||
DW_FORM_str2 => Some(AttrValue::Unsigned(data.read_u16()?.into())),
|
||||
DW_FORM_str3 => Some(AttrValue::Unsigned(read_3bytes(data)?.into())),
|
||||
DW_FORM_str4 => Some(AttrValue::Unsigned(data.read_u32()?.into())),
|
||||
DW_FORM_addrx1 => Some(AttrValue::Unsigned(data.read_u8()?.into())),
|
||||
DW_FORM_addrx2 => Some(AttrValue::Unsigned(data.read_u16()?.into())),
|
||||
DW_FORM_addrx3 => Some(AttrValue::Unsigned(read_3bytes(data)?.into())),
|
||||
DW_FORM_addrx4 => Some(AttrValue::Unsigned(data.read_u32()?.into())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the value of an attribute from a data buffer.
|
||||
///
|
||||
/// This function works with [`parse_abbrev_attr()`], that parse the
|
||||
/// attribute specifications of DIEs delcared in the abbreviation
|
||||
/// table in the .debug_abbrev section, by using the result of
|
||||
/// [`parse_abbrev_attr()`] to parse the value of an attribute of a
|
||||
/// DIE.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data` - A buffer where the value is in.
|
||||
/// * `form` - The format of the value. (DW_FORM_*)
|
||||
/// * `dwarf_sz` - Describe the DWARF format. (4 for 32-bits and 8 for 64-bits)
|
||||
/// * `addr_sz` - The size of an address of the target platform. (4 for 32-bits and 8 for 64-bits)
|
||||
///
|
||||
/// Return AttrValue and the number of bytes it takes.
|
||||
fn extract_attr_value(
|
||||
mut data: &[u8],
|
||||
form: u8,
|
||||
dwarf_sz: usize,
|
||||
addr_sz: usize,
|
||||
) -> Option<(AttrValue<'_>, usize)> {
|
||||
let data = &mut data;
|
||||
let before = (*data).as_ptr();
|
||||
let value = extract_attr_value_impl(data, form, dwarf_sz, addr_sz)?;
|
||||
let after = (*data).as_ptr();
|
||||
// TODO: Remove this workaround once callers no longer require an explicit
|
||||
// byte count being passed out.
|
||||
// SAFETY: Both pointers point into the same underlying byte array.
|
||||
let count = unsafe { after.offset_from(before) };
|
||||
Some((value, count.try_into().unwrap()))
|
||||
}
|
||||
|
||||
/// Parse all abbreviations of an abbreviation table for a compile
|
||||
/// unit.
|
||||
///
|
||||
/// An abbreviation table is usually for a compile unit, but not always.
|
||||
///
|
||||
/// Return a list of abbreviations and the number of bytes they take.
|
||||
fn parse_cu_abbrevs(data: &[u8]) -> Option<(Vec<Abbrev>, usize)> {
|
||||
let mut pos = 0;
|
||||
let mut abbrevs = Vec::<Abbrev>::with_capacity(data.len() / 50); // Heuristic!
|
||||
|
||||
while pos < data.len() {
|
||||
let (abbrev, bytes) = parse_abbrev(&data[pos..])?;
|
||||
pos += bytes;
|
||||
if abbrev.abbrev_code == 0x0 {
|
||||
return Some((abbrevs, pos));
|
||||
}
|
||||
abbrevs.push(abbrev);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Parse an Unit Header from a buffer.
|
||||
///
|
||||
/// An Unit Header is the header of a compile unit, at leat for v4.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data` - is the data from the `.debug_info` section.
|
||||
fn parse_unit_header(data: &[u8]) -> Option<UnitHeader> {
|
||||
if data.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut pos = 0;
|
||||
let mut init_length = decode_uword(data) as usize;
|
||||
pos += 4;
|
||||
|
||||
let bits64 = init_length == 0xffffffff;
|
||||
if bits64 {
|
||||
if (pos + 8) > data.len() {
|
||||
return None;
|
||||
}
|
||||
init_length = decode_udword(&data[pos..]) as usize;
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
if (pos + 2) > data.len() {
|
||||
return None;
|
||||
}
|
||||
let version = decode_uhalf(&data[pos..]);
|
||||
pos += 2;
|
||||
|
||||
if version == 0x4 {
|
||||
let debug_abbrev_offset: u64 = if bits64 {
|
||||
if (pos + 8) > data.len() {
|
||||
return None;
|
||||
}
|
||||
let v = decode_udword(&data[pos..]);
|
||||
pos += 8;
|
||||
v
|
||||
} else {
|
||||
if (pos + 4) > data.len() {
|
||||
return None;
|
||||
}
|
||||
let v = decode_uword(&data[pos..]);
|
||||
pos += 4;
|
||||
v as u64
|
||||
};
|
||||
let address_size = data[pos];
|
||||
pos += 1;
|
||||
return Some(UnitHeader::CompileV4(CUHeaderV4 {
|
||||
init_length,
|
||||
bits64,
|
||||
version,
|
||||
debug_abbrev_offset,
|
||||
address_size,
|
||||
hdr_size: pos,
|
||||
}));
|
||||
}
|
||||
|
||||
if (pos + 1) > data.len() {
|
||||
return None;
|
||||
}
|
||||
let unit_type = data[pos];
|
||||
pos += 1;
|
||||
|
||||
match unit_type {
|
||||
DW_UT_compile => {
|
||||
if (pos + 1) > data.len() {
|
||||
return None;
|
||||
}
|
||||
let address_size = data[pos];
|
||||
pos += 1;
|
||||
|
||||
let debug_abbrev_offset: u64 = if bits64 {
|
||||
if (pos + 8) > data.len() {
|
||||
return None;
|
||||
}
|
||||
let v = decode_udword(&data[pos..]);
|
||||
pos += 8;
|
||||
v
|
||||
} else {
|
||||
if (pos + 4) > data.len() {
|
||||
return None;
|
||||
}
|
||||
let v = decode_uword(&data[pos..]);
|
||||
pos += 4;
|
||||
v as u64
|
||||
};
|
||||
Some(UnitHeader::CompileV5(CUHeaderV5 {
|
||||
init_length,
|
||||
bits64,
|
||||
version,
|
||||
unit_type,
|
||||
address_size,
|
||||
debug_abbrev_offset,
|
||||
hdr_size: pos,
|
||||
}))
|
||||
}
|
||||
_ => Some(UnitHeader::Unknown(UnknownHeader {
|
||||
init_length,
|
||||
bits64,
|
||||
version,
|
||||
unit_type,
|
||||
hdr_size: pos,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug Information Entry.
|
||||
///
|
||||
/// A DIE starts with the code of its abbreviation followed by the
|
||||
/// attribute values described by the abbreviation. The code of an
|
||||
/// abbreviation is an index to the abbreviation table of the compile
|
||||
/// unit.
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct DIE<'a> {
|
||||
pub tag: u8,
|
||||
pub abbrev: Option<&'a Abbrev>,
|
||||
abbrev_attrs: &'a [AbbrevAttr],
|
||||
abbrev_attrs_idx: usize,
|
||||
data: &'a [u8],
|
||||
dieiter: &'a mut DIEIter<'a>,
|
||||
reading_offset: usize,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl<'a> DIE<'a> {
|
||||
#[inline]
|
||||
pub fn exhaust(&mut self) -> Result<(), Error> {
|
||||
let abbrev_attrs = self.abbrev_attrs;
|
||||
|
||||
if self.done {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
while self.abbrev_attrs_idx < abbrev_attrs.len() {
|
||||
let attr = &abbrev_attrs[self.abbrev_attrs_idx];
|
||||
self.abbrev_attrs_idx += 1;
|
||||
|
||||
if attr.form == 0 {
|
||||
continue;
|
||||
}
|
||||
let (_value, bytes) = extract_attr_value(
|
||||
&self.data[self.reading_offset..],
|
||||
attr.form,
|
||||
self.dieiter.dwarf_sz,
|
||||
self.dieiter.addr_sz,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
Error::new(ErrorKind::InvalidData, "failed to parse attribute values")
|
||||
})?;
|
||||
self.reading_offset += bytes;
|
||||
}
|
||||
self.dieiter.die_finish_reading(self.reading_offset);
|
||||
self.done = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DIE<'a> {
|
||||
// name, form, opt, value
|
||||
type Item = (u8, u8, u128, AttrValue<'a>);
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.done {
|
||||
return None;
|
||||
}
|
||||
self.abbrev?;
|
||||
|
||||
if self.abbrev_attrs_idx < self.abbrev_attrs.len() {
|
||||
let AbbrevAttr { name, form, opt } = self.abbrev_attrs[self.abbrev_attrs_idx];
|
||||
self.abbrev_attrs_idx += 1;
|
||||
|
||||
#[cfg(debug)]
|
||||
if form == 0 {
|
||||
assert_eq!(self.abbrev_off, abbrev.attrs.len());
|
||||
}
|
||||
if form == 0 {
|
||||
self.dieiter.die_finish_reading(self.reading_offset);
|
||||
self.done = true;
|
||||
return None;
|
||||
}
|
||||
|
||||
let (value, bytes) = extract_attr_value(
|
||||
&self.data[self.reading_offset..],
|
||||
form,
|
||||
self.dieiter.dwarf_sz,
|
||||
self.dieiter.addr_sz,
|
||||
)?;
|
||||
self.reading_offset += bytes;
|
||||
Some((name, form, opt, value))
|
||||
} else {
|
||||
self.dieiter.die_finish_reading(self.reading_offset);
|
||||
self.done = true;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The iterator of DIEs in an Unit.
|
||||
pub struct DIEIter<'a> {
|
||||
data: &'a [u8],
|
||||
dwarf_sz: usize,
|
||||
addr_sz: usize,
|
||||
off: usize,
|
||||
off_delta: usize,
|
||||
cur_depth: usize,
|
||||
abbrevs: Vec<Abbrev>,
|
||||
abbrev: Option<&'a Abbrev>,
|
||||
die_reading_done: bool,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl<'a> DIEIter<'a> {
|
||||
pub fn die_finish_reading(&mut self, size: usize) {
|
||||
self.die_reading_done = true;
|
||||
self.off += size;
|
||||
}
|
||||
|
||||
pub fn seek_to_sibling(&mut self, off: usize) {
|
||||
self.off = off - self.off_delta;
|
||||
self.cur_depth -= 1;
|
||||
self.die_reading_done = true;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn exhaust_die(&mut self) -> Result<(), Error> {
|
||||
assert!(
|
||||
!self.die_reading_done,
|
||||
"DIE should not have been exhausted!"
|
||||
);
|
||||
let abbrev = self.abbrev.unwrap();
|
||||
for attr in abbrev.all_attrs() {
|
||||
if attr.form == 0 {
|
||||
continue;
|
||||
}
|
||||
let (_value, bytes) = extract_attr_value(
|
||||
&self.data[self.off..],
|
||||
attr.form,
|
||||
self.dwarf_sz,
|
||||
self.addr_sz,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
Error::new(ErrorKind::InvalidData, "failed to parse attribute values")
|
||||
})?;
|
||||
self.off += bytes;
|
||||
}
|
||||
self.die_reading_done = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DIEIter<'a> {
|
||||
type Item = DIE<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.die_reading_done {
|
||||
self.exhaust_die().unwrap();
|
||||
}
|
||||
if self.done {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (abbrev_idx, bytes) = decode_leb128_128(&self.data[self.off..])?;
|
||||
self.off += bytes as usize;
|
||||
|
||||
if abbrev_idx == 0 {
|
||||
self.cur_depth -= 1;
|
||||
if self.cur_depth == 0 {
|
||||
self.done = true;
|
||||
}
|
||||
Some(DIE {
|
||||
tag: 0,
|
||||
abbrev: None,
|
||||
abbrev_attrs: &[],
|
||||
abbrev_attrs_idx: 0,
|
||||
data: &self.data[self.off..],
|
||||
dieiter: unsafe { mem::transmute(self) },
|
||||
reading_offset: 0,
|
||||
done: false,
|
||||
})
|
||||
} else {
|
||||
let abbrev = unsafe { mem::transmute(&self.abbrevs[abbrev_idx as usize - 1]) };
|
||||
self.abbrev = Some(abbrev);
|
||||
if abbrev.has_children {
|
||||
self.cur_depth += 1;
|
||||
}
|
||||
|
||||
self.die_reading_done = false;
|
||||
Some(DIE {
|
||||
tag: abbrev.tag,
|
||||
abbrev: Some(abbrev),
|
||||
abbrev_attrs: abbrev.all_attrs(),
|
||||
abbrev_attrs_idx: 0,
|
||||
data: &self.data[self.off..],
|
||||
dieiter: unsafe { mem::transmute(self) },
|
||||
reading_offset: 0,
|
||||
done: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator of Units in the `.debug_info` section.
|
||||
///
|
||||
/// An iterator is built from the content of `.debug_info` section,
|
||||
/// which is a list of compile units. A compile unit usually refers
|
||||
/// to a source file. In the compile units, it is a forest of DIEs,
|
||||
/// which presents functions, variables and other debug information.
|
||||
pub struct UnitIter<'a> {
|
||||
info_data: &'a [u8],
|
||||
abbrev_data: &'a [u8],
|
||||
off: usize,
|
||||
}
|
||||
|
||||
impl<'a> UnitIter<'a> {
|
||||
/// Build an iterator from the content of `.debug_info` & `.debug_abbrev`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `info_data` is the content of the `.debug_info` section.
|
||||
/// * `abbrev_data` is the content of the `.debug_abbrev` section.
|
||||
pub fn new(info_data: &'a [u8], abbrev_data: &'a [u8]) -> UnitIter<'a> {
|
||||
UnitIter {
|
||||
info_data,
|
||||
abbrev_data,
|
||||
off: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for UnitIter<'a> {
|
||||
type Item = (UnitHeader, DIEIter<'a>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let off = self.off;
|
||||
let uh = parse_unit_header(&self.info_data[off..])?;
|
||||
let hdr_sz = uh.header_size();
|
||||
self.off += uh.unit_size();
|
||||
|
||||
match uh {
|
||||
UnitHeader::CompileV4(ref cuh) => {
|
||||
let dwarf_sz = if cuh.bits64 { 8 } else { 4 };
|
||||
let addr_sz = cuh.address_size as usize;
|
||||
let (abbrevs, _) =
|
||||
parse_cu_abbrevs(&self.abbrev_data[cuh.debug_abbrev_offset as usize..])?;
|
||||
Some((
|
||||
uh,
|
||||
DIEIter {
|
||||
data: &self.info_data[off + hdr_sz..],
|
||||
dwarf_sz,
|
||||
addr_sz,
|
||||
off: 0,
|
||||
off_delta: hdr_sz,
|
||||
cur_depth: 0,
|
||||
abbrevs,
|
||||
abbrev: None,
|
||||
die_reading_done: true,
|
||||
done: false,
|
||||
},
|
||||
))
|
||||
}
|
||||
UnitHeader::CompileV5(ref _cuh) => {
|
||||
todo!(); // BlazeSym supports only v4 so far.
|
||||
}
|
||||
_ => self.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::elf::ElfParser;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn test_parse_abbrev() {
|
||||
let raw = [
|
||||
0x01, 0x11, 0x01, 0x25, 0x0e, 0x13, 0x05, 0x03, 0x0e, 0x10, 0x17, 0x1b, 0x0e, 0xb4,
|
||||
0x42, 0x19, 0x11, 0x01, 0x55, 0x17, 0x00, 0x00, 0x02, 0x39, 0x01, 0x03, 0x0e, 0x00,
|
||||
0x00, 0x03, 0x04, 0x01, 0x49, 0x13, 0x6d, 0x19, 0x03, 0x0e, 0x0b, 0x0b, 0x88, 0x01,
|
||||
0x0f, 0x00, 0x00, 0x04, 0x28, 0x00, 0x03, 0x0e, 0x1c, 0x0f, 0x00, 0x00, 0x05, 0x13,
|
||||
0x01, 0x03, 0x0e, 0x0b, 0x0b, 0x88, 0x01, 0x0f, 0x00, 0x00,
|
||||
];
|
||||
let (abbrev, bytes) = parse_abbrev(&raw).unwrap();
|
||||
assert_eq!(bytes, 22);
|
||||
assert_eq!(abbrev.abbrev_code, 0x1);
|
||||
assert_eq!(abbrev.tag, DW_TAG_compile_unit);
|
||||
assert!(abbrev.has_children);
|
||||
let mut pos = bytes;
|
||||
|
||||
let (abbrev, bytes) = parse_abbrev(&raw[pos..]).unwrap();
|
||||
assert_eq!(bytes, 7);
|
||||
assert_eq!(abbrev.abbrev_code, 0x2);
|
||||
assert_eq!(abbrev.tag, DW_TAG_namespace);
|
||||
assert!(abbrev.has_children);
|
||||
pos += bytes;
|
||||
|
||||
let (abbrev, bytes) = parse_abbrev(&raw[pos..]).unwrap();
|
||||
assert_eq!(bytes, 16);
|
||||
assert_eq!(abbrev.abbrev_code, 0x3);
|
||||
assert_eq!(abbrev.tag, DW_TAG_enumeration_type);
|
||||
assert!(abbrev.has_children);
|
||||
pos += bytes;
|
||||
|
||||
let (abbrev, bytes) = parse_abbrev(&raw[pos..]).unwrap();
|
||||
assert_eq!(bytes, 9);
|
||||
assert_eq!(abbrev.abbrev_code, 0x4);
|
||||
assert!(!abbrev.has_children);
|
||||
pos += bytes;
|
||||
|
||||
let (abbrev, bytes) = parse_abbrev(&raw[pos..]).unwrap();
|
||||
assert_eq!(bytes, 12);
|
||||
assert_eq!(abbrev.abbrev_code, 0x5);
|
||||
assert!(abbrev.has_children);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_cu_abbrevs() {
|
||||
let raw = [
|
||||
0x01, 0x11, 0x01, 0x25, 0x0e, 0x13, 0x05, 0x03, 0x0e, 0x10, 0x17, 0x1b, 0x0e, 0xb4,
|
||||
0x42, 0x19, 0x11, 0x01, 0x55, 0x17, 0x00, 0x00, 0x02, 0x39, 0x01, 0x03, 0x0e, 0x00,
|
||||
0x00, 0x03, 0x04, 0x01, 0x49, 0x13, 0x6d, 0x19, 0x03, 0x0e, 0x0b, 0x0b, 0x88, 0x01,
|
||||
0x0f, 0x00, 0x00, 0x04, 0x28, 0x00, 0x03, 0x0e, 0x1c, 0x0f, 0x00, 0x00, 0x05, 0x13,
|
||||
0x01, 0x03, 0x0e, 0x0b, 0x0b, 0x88, 0x01, 0x0f, 0x00, 0x00, 0x00,
|
||||
];
|
||||
let (abbrevs, bytes) = parse_cu_abbrevs(&raw).unwrap();
|
||||
assert_eq!(abbrevs.len(), 0x5);
|
||||
assert_eq!(bytes, raw.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unititer() {
|
||||
let bin_name = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("dwarf-example");
|
||||
let elfparser = ElfParser::open(&bin_name).unwrap();
|
||||
let abbrev_idx = elfparser.find_section(".debug_abbrev").unwrap();
|
||||
let abbrev = elfparser.read_section_raw(abbrev_idx).unwrap();
|
||||
let info_idx = elfparser.find_section(".debug_info").unwrap();
|
||||
let info = elfparser.read_section_raw(info_idx).unwrap();
|
||||
|
||||
let iter = UnitIter::new(&info, &abbrev);
|
||||
let mut cnt = 0;
|
||||
let mut die_cnt = 0;
|
||||
let mut attr_cnt = 0;
|
||||
let mut subprog_cnt = 0;
|
||||
for (_uh, dieiter) in iter {
|
||||
cnt += 1;
|
||||
for die in dieiter {
|
||||
die_cnt += 1;
|
||||
if die.tag == DW_TAG_subprogram {
|
||||
subprog_cnt += 1;
|
||||
}
|
||||
for (_name, _form, _opt, _value) in die {
|
||||
attr_cnt += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(cnt, 9);
|
||||
assert_eq!(die_cnt, 78752);
|
||||
assert_eq!(subprog_cnt, 12451);
|
||||
assert_eq!(attr_cnt, 275310);
|
||||
}
|
||||
}
|
||||
312
third_party/blazesym/src/elf/cache.rs
vendored
Normal file
312
third_party/blazesym/src/elf/cache.rs
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Error;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use nix::sys::stat::{fstat, FileStat};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use crate::dwarf::DwarfResolver;
|
||||
|
||||
use super::ElfParser;
|
||||
|
||||
type ElfCacheEntryKey = PathBuf;
|
||||
|
||||
const DFL_CACHE_MAX: usize = 1024;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ElfBackend {
|
||||
Dwarf(Rc<DwarfResolver>), // ELF w/ DWARF
|
||||
Elf(Rc<ElfParser>), // ELF w/o DWARF
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl ElfBackend {
|
||||
pub fn to_dwarf(&self) -> Option<Rc<DwarfResolver>> {
|
||||
if let Self::Dwarf(dwarf) = self {
|
||||
Some(Rc::clone(dwarf))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dwarf(&self) -> bool {
|
||||
matches!(self, Self::Dwarf(_))
|
||||
}
|
||||
}
|
||||
|
||||
struct ElfCacheEntry {
|
||||
// LRU links
|
||||
prev: *mut ElfCacheEntry,
|
||||
next: *mut ElfCacheEntry,
|
||||
|
||||
file_name: PathBuf,
|
||||
|
||||
dev: libc::dev_t,
|
||||
inode: libc::ino_t,
|
||||
size: libc::off_t,
|
||||
mtime_sec: libc::time_t,
|
||||
mtime_nsec: i64,
|
||||
backend: ElfBackend,
|
||||
}
|
||||
|
||||
impl ElfCacheEntry {
|
||||
pub fn new(
|
||||
file_name: &Path,
|
||||
file: File,
|
||||
line_number_info: bool,
|
||||
debug_info_symbols: bool,
|
||||
) -> Result<ElfCacheEntry, Error> {
|
||||
let stat = fstat(file.as_raw_fd())?;
|
||||
let parser = Rc::new(ElfParser::open_file(file)?);
|
||||
let backend = if let Ok(dwarf) = DwarfResolver::from_parser_for_addresses(
|
||||
Rc::clone(&parser),
|
||||
&[],
|
||||
line_number_info,
|
||||
debug_info_symbols,
|
||||
) {
|
||||
ElfBackend::Dwarf(Rc::new(dwarf))
|
||||
} else {
|
||||
ElfBackend::Elf(parser)
|
||||
};
|
||||
|
||||
Ok(ElfCacheEntry {
|
||||
prev: ptr::null_mut(),
|
||||
next: ptr::null_mut(),
|
||||
file_name: file_name.to_path_buf(),
|
||||
dev: stat.st_dev,
|
||||
inode: stat.st_ino,
|
||||
size: stat.st_size,
|
||||
mtime_sec: stat.st_mtime,
|
||||
mtime_nsec: stat.st_mtime_nsec,
|
||||
backend,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_key(&self) -> &Path {
|
||||
&self.file_name
|
||||
}
|
||||
|
||||
fn is_valid(&self, stat: &FileStat) -> bool {
|
||||
stat.st_dev == self.dev
|
||||
&& stat.st_ino == self.inode
|
||||
&& stat.st_size == self.size
|
||||
&& stat.st_mtime == self.mtime_sec
|
||||
&& stat.st_mtime_nsec == self.mtime_nsec
|
||||
}
|
||||
|
||||
fn get_backend(&self) -> ElfBackend {
|
||||
self.backend.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Maintain a LRU linked list of entries
|
||||
struct ElfCacheLru {
|
||||
head: *mut ElfCacheEntry,
|
||||
tail: *mut ElfCacheEntry,
|
||||
}
|
||||
|
||||
impl ElfCacheLru {
|
||||
/// # Safety
|
||||
///
|
||||
/// Make all entries are valid.
|
||||
unsafe fn touch(&mut self, ent: &ElfCacheEntry) {
|
||||
unsafe { self.remove(ent) };
|
||||
unsafe { self.push_back(ent) };
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Make all entries are valid.
|
||||
unsafe fn remove(&mut self, ent: &ElfCacheEntry) {
|
||||
let ent_ptr = ent as *const ElfCacheEntry as *mut ElfCacheEntry;
|
||||
let prev = unsafe { (*ent_ptr).prev };
|
||||
let next = unsafe { (*ent_ptr).next };
|
||||
if !prev.is_null() {
|
||||
unsafe { (*prev).next = next };
|
||||
} else {
|
||||
self.head = next;
|
||||
}
|
||||
if !next.is_null() {
|
||||
unsafe { (*next).prev = prev };
|
||||
} else {
|
||||
self.tail = prev;
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Make all entries are valid.
|
||||
unsafe fn push_back(&mut self, ent: &ElfCacheEntry) {
|
||||
let ent_ptr = ent as *const ElfCacheEntry as *mut ElfCacheEntry;
|
||||
if self.head.is_null() {
|
||||
unsafe { (*ent_ptr).next = ptr::null_mut() };
|
||||
unsafe { (*ent_ptr).prev = ptr::null_mut() };
|
||||
self.head = ent_ptr;
|
||||
self.tail = ent_ptr;
|
||||
} else {
|
||||
unsafe { (*ent_ptr).next = ptr::null_mut() };
|
||||
unsafe { (*self.tail).next = ent_ptr };
|
||||
unsafe { (*ent_ptr).prev = self.tail };
|
||||
self.tail = ent_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Make all entries are valid.
|
||||
unsafe fn pop_head(&mut self) -> *mut ElfCacheEntry {
|
||||
let ent = self.head;
|
||||
if !ent.is_null() {
|
||||
unsafe { self.remove(&*ent) };
|
||||
}
|
||||
ent
|
||||
}
|
||||
}
|
||||
|
||||
struct _ElfCache {
|
||||
elfs: HashMap<ElfCacheEntryKey, Box<ElfCacheEntry>>,
|
||||
lru: ElfCacheLru,
|
||||
max_elfs: usize,
|
||||
line_number_info: bool,
|
||||
debug_info_symbols: bool,
|
||||
}
|
||||
|
||||
impl _ElfCache {
|
||||
fn new(line_number_info: bool, debug_info_symbols: bool) -> _ElfCache {
|
||||
_ElfCache {
|
||||
elfs: HashMap::new(),
|
||||
lru: ElfCacheLru {
|
||||
head: ptr::null_mut(),
|
||||
tail: ptr::null_mut(),
|
||||
},
|
||||
max_elfs: DFL_CACHE_MAX,
|
||||
line_number_info,
|
||||
debug_info_symbols,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned reference is only valid before next time calling
|
||||
/// create_entry().
|
||||
///
|
||||
unsafe fn find_entry(&mut self, file_name: &Path) -> Option<&ElfCacheEntry> {
|
||||
let ent = self.elfs.get(file_name)?;
|
||||
unsafe { self.lru.touch(ent) };
|
||||
|
||||
Some(ent.as_ref())
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The returned reference is only valid before next time calling
|
||||
/// create_entry().
|
||||
///
|
||||
unsafe fn create_entry(
|
||||
&mut self,
|
||||
file_name: &Path,
|
||||
file: File,
|
||||
) -> Result<&ElfCacheEntry, Error> {
|
||||
let ent = Box::new(ElfCacheEntry::new(
|
||||
file_name,
|
||||
file,
|
||||
self.line_number_info,
|
||||
self.debug_info_symbols,
|
||||
)?);
|
||||
let key = ent.get_key().to_path_buf();
|
||||
|
||||
self.elfs.insert(key.clone(), ent);
|
||||
unsafe { self.lru.push_back(self.elfs.get(&key).unwrap().as_ref()) };
|
||||
unsafe { self.ensure_size() };
|
||||
|
||||
Ok(unsafe { &*self.lru.tail }) // Get 'static lifetime
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This funciton may make some cache entries invalid. Callers
|
||||
/// should be careful about all references of cache entries they
|
||||
/// are holding.
|
||||
unsafe fn ensure_size(&mut self) {
|
||||
if self.elfs.len() > self.max_elfs {
|
||||
let to_remove = unsafe { self.lru.pop_head() };
|
||||
self.elfs.remove(unsafe { (*to_remove).get_key() }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn find_or_create_backend(
|
||||
&mut self,
|
||||
file_name: &Path,
|
||||
file: File,
|
||||
) -> Result<ElfBackend, Error> {
|
||||
if let Some(ent) = unsafe { self.find_entry(file_name) } {
|
||||
let stat = fstat(file.as_raw_fd())?;
|
||||
|
||||
if ent.is_valid(&stat) {
|
||||
return Ok(ent.get_backend());
|
||||
}
|
||||
|
||||
// Purge the entry and load it from the filesystem.
|
||||
unsafe {
|
||||
let ent = &*(ent as *const ElfCacheEntry); // static lifetime to decouple borrowing
|
||||
self.lru.remove(ent)
|
||||
};
|
||||
self.elfs.remove(file_name);
|
||||
}
|
||||
|
||||
Ok(unsafe { self.create_entry(file_name, file)? }.get_backend())
|
||||
}
|
||||
|
||||
pub fn find(&mut self, path: &Path) -> Result<ElfBackend, Error> {
|
||||
let file = File::open(path)?;
|
||||
self.find_or_create_backend(path, file)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ElfCache {
|
||||
cache: RefCell<_ElfCache>,
|
||||
}
|
||||
|
||||
impl ElfCache {
|
||||
pub fn new(line_number_info: bool, debug_info_symbols: bool) -> ElfCache {
|
||||
ElfCache {
|
||||
cache: RefCell::new(_ElfCache::new(line_number_info, debug_info_symbols)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find(&self, path: &Path) -> Result<ElfBackend, Error> {
|
||||
let mut cache = self.cache.borrow_mut();
|
||||
cache.find(path)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::env;
|
||||
|
||||
#[test]
|
||||
fn test_cache() {
|
||||
let bin_name = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test-no-debug.bin");
|
||||
|
||||
let cache = ElfCache::new(true, false);
|
||||
let backend_first = cache.find(Path::new(&bin_name));
|
||||
let backend_second = cache.find(Path::new(&bin_name));
|
||||
assert!(backend_first.is_ok());
|
||||
assert!(backend_second.is_ok());
|
||||
let backend_first = backend_first.unwrap();
|
||||
let backend_second = backend_second.unwrap();
|
||||
assert!(backend_first.is_dwarf());
|
||||
assert!(backend_second.is_dwarf());
|
||||
assert_eq!(
|
||||
ptr::addr_of!(*backend_first.to_dwarf().unwrap().get_parser()),
|
||||
ptr::addr_of!(*backend_second.to_dwarf().unwrap().get_parser())
|
||||
);
|
||||
}
|
||||
}
|
||||
8
third_party/blazesym/src/elf/mod.rs
vendored
Normal file
8
third_party/blazesym/src/elf/mod.rs
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
mod cache;
|
||||
mod parser;
|
||||
mod resolver;
|
||||
mod types;
|
||||
|
||||
pub use cache::ElfCache;
|
||||
pub use parser::ElfParser;
|
||||
pub use resolver::ElfResolver;
|
||||
699
third_party/blazesym/src/elf/parser.rs
vendored
Normal file
699
third_party/blazesym/src/elf/parser.rs
vendored
Normal file
@@ -0,0 +1,699 @@
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind, Read, Seek, SeekFrom};
|
||||
use std::mem;
|
||||
#[cfg(test)]
|
||||
use std::path::Path;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use crate::util::{extract_string, search_address_opt_key};
|
||||
use crate::FindAddrOpts;
|
||||
use crate::SymbolInfo;
|
||||
use crate::SymbolType;
|
||||
|
||||
use super::types::Elf64_Ehdr;
|
||||
use super::types::Elf64_Phdr;
|
||||
use super::types::Elf64_Shdr;
|
||||
use super::types::Elf64_Sym;
|
||||
use super::types::SHN_UNDEF;
|
||||
#[cfg(test)]
|
||||
use super::types::STT_FUNC;
|
||||
|
||||
|
||||
fn read_u8(file: &mut File, off: u64, size: usize) -> Result<Vec<u8>, Error> {
|
||||
let mut buf = vec![0; size];
|
||||
|
||||
file.seek(SeekFrom::Start(off))?;
|
||||
file.read_exact(buf.as_mut_slice())?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn read_elf_header(file: &mut File) -> Result<Elf64_Ehdr, Error> {
|
||||
let mut buffer = [0u8; mem::size_of::<Elf64_Ehdr>()];
|
||||
let () = file.read_exact(&mut buffer)?;
|
||||
|
||||
let pointer = buffer.as_ptr() as *const Elf64_Ehdr;
|
||||
// SAFETY: `buffer` is valid for reads and the `Elf64_Ehdr` object that we
|
||||
// read is comprised only of members that are valid for any bit
|
||||
// pattern.
|
||||
let elf_header = unsafe { pointer.read_unaligned() };
|
||||
|
||||
Ok(elf_header)
|
||||
}
|
||||
|
||||
fn read_elf_sections(file: &mut File, ehdr: &Elf64_Ehdr) -> Result<Vec<Elf64_Shdr>, Error> {
|
||||
const HDRSIZE: usize = mem::size_of::<Elf64_Shdr>();
|
||||
let off = ehdr.e_shoff as usize;
|
||||
let num = ehdr.e_shnum as usize;
|
||||
|
||||
let mut buf = read_u8(file, off as u64, num * HDRSIZE)?;
|
||||
|
||||
let shdrs: Vec<Elf64_Shdr> = unsafe {
|
||||
let shdrs_ptr = buf.as_mut_ptr() as *mut Elf64_Shdr;
|
||||
buf.leak();
|
||||
Vec::from_raw_parts(shdrs_ptr, num, num)
|
||||
};
|
||||
Ok(shdrs)
|
||||
}
|
||||
|
||||
fn read_elf_program_headers(file: &mut File, ehdr: &Elf64_Ehdr) -> Result<Vec<Elf64_Phdr>, Error> {
|
||||
const HDRSIZE: usize = mem::size_of::<Elf64_Phdr>();
|
||||
let off = ehdr.e_phoff as usize;
|
||||
let num = ehdr.e_phnum as usize;
|
||||
|
||||
let mut buf = read_u8(file, off as u64, num * HDRSIZE)?;
|
||||
|
||||
let phdrs: Vec<Elf64_Phdr> = unsafe {
|
||||
let phdrs_ptr = buf.as_mut_ptr() as *mut Elf64_Phdr;
|
||||
buf.leak();
|
||||
Vec::from_raw_parts(phdrs_ptr, num, num)
|
||||
};
|
||||
Ok(phdrs)
|
||||
}
|
||||
|
||||
fn read_elf_section_raw(file: &mut File, section: &Elf64_Shdr) -> Result<Vec<u8>, Error> {
|
||||
read_u8(file, section.sh_offset, section.sh_size as usize)
|
||||
}
|
||||
|
||||
fn read_elf_section_seek(file: &mut File, section: &Elf64_Shdr) -> Result<(), Error> {
|
||||
file.seek(SeekFrom::Start(section.sh_offset))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_elf_section_name<'a>(sect: &Elf64_Shdr, strtab: &'a [u8]) -> Option<&'a str> {
|
||||
extract_string(strtab, sect.sh_name as usize)
|
||||
}
|
||||
|
||||
struct ElfParserBack {
|
||||
ehdr: Option<Elf64_Ehdr>,
|
||||
shdrs: Option<Vec<Elf64_Shdr>>,
|
||||
shstrtab: Option<Vec<u8>>,
|
||||
phdrs: Option<Vec<Elf64_Phdr>>,
|
||||
symtab: Option<Vec<Elf64_Sym>>, // in address order
|
||||
symtab_origin: Option<Vec<Elf64_Sym>>, // The copy in the same order as the file
|
||||
strtab: Option<Vec<u8>>,
|
||||
str2symtab: Option<Vec<(usize, usize)>>, // strtab offset to symtab in the dictionary order
|
||||
sect_cache: Vec<Option<Vec<u8>>>,
|
||||
}
|
||||
|
||||
/// A parser against ELF64 format.
|
||||
///
|
||||
pub struct ElfParser {
|
||||
file: RefCell<File>,
|
||||
backobj: RefCell<ElfParserBack>,
|
||||
}
|
||||
|
||||
impl ElfParser {
|
||||
pub fn open_file(file: File) -> Result<ElfParser, Error> {
|
||||
let parser = ElfParser {
|
||||
file: RefCell::new(file),
|
||||
backobj: RefCell::new(ElfParserBack {
|
||||
ehdr: None,
|
||||
shdrs: None,
|
||||
shstrtab: None,
|
||||
phdrs: None,
|
||||
symtab: None,
|
||||
symtab_origin: None,
|
||||
strtab: None,
|
||||
str2symtab: None,
|
||||
sect_cache: vec![],
|
||||
}),
|
||||
};
|
||||
Ok(parser)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn open(filename: &Path) -> Result<ElfParser, Error> {
|
||||
let file = File::open(filename)?;
|
||||
let parser = Self::open_file(file);
|
||||
if let Ok(parser) = parser {
|
||||
Ok(parser)
|
||||
} else {
|
||||
parser
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_ehdr(&self) -> Result<(), Error> {
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
|
||||
if me.ehdr.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ehdr = read_elf_header(&mut self.file.borrow_mut())?;
|
||||
if !(ehdr.e_ident[0] == 0x7f
|
||||
&& ehdr.e_ident[1] == 0x45
|
||||
&& ehdr.e_ident[2] == 0x4c
|
||||
&& ehdr.e_ident[3] == 0x46)
|
||||
{
|
||||
return Err(Error::new(ErrorKind::InvalidData, "e_ident is wrong"));
|
||||
}
|
||||
|
||||
me.ehdr = Some(ehdr);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_shdrs(&self) -> Result<(), Error> {
|
||||
self.ensure_ehdr()?;
|
||||
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
|
||||
if me.shdrs.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shdrs = read_elf_sections(&mut self.file.borrow_mut(), me.ehdr.as_ref().unwrap())?;
|
||||
me.sect_cache.resize(shdrs.len(), None);
|
||||
me.shdrs = Some(shdrs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_phdrs(&self) -> Result<(), Error> {
|
||||
self.ensure_ehdr()?;
|
||||
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
|
||||
if me.phdrs.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let phdrs =
|
||||
read_elf_program_headers(&mut self.file.borrow_mut(), me.ehdr.as_ref().unwrap())?;
|
||||
me.phdrs = Some(phdrs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_shstrtab(&self) -> Result<(), Error> {
|
||||
self.ensure_shdrs()?;
|
||||
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
|
||||
if me.shstrtab.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shstrndx = me.ehdr.as_ref().unwrap().e_shstrndx;
|
||||
let shstrtab_sec = &me.shdrs.as_ref().unwrap()[shstrndx as usize];
|
||||
let shstrtab = read_elf_section_raw(&mut self.file.borrow_mut(), shstrtab_sec)?;
|
||||
me.shstrtab = Some(shstrtab);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_symtab(&self) -> Result<(), Error> {
|
||||
{
|
||||
let me = self.backobj.borrow();
|
||||
|
||||
if me.symtab.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let sect_idx = if let Ok(idx) = self.find_section(".symtab") {
|
||||
idx
|
||||
} else {
|
||||
self.find_section(".dynsym")?
|
||||
};
|
||||
let symtab_raw = self.read_section_raw(sect_idx)?;
|
||||
|
||||
if symtab_raw.len() % mem::size_of::<Elf64_Sym>() != 0 {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"size of the .symtab section does not match",
|
||||
));
|
||||
}
|
||||
let cnt = symtab_raw.len() / mem::size_of::<Elf64_Sym>();
|
||||
let mut symtab: Vec<Elf64_Sym> = unsafe {
|
||||
let symtab_ptr = symtab_raw.as_ptr() as *mut Elf64_Sym;
|
||||
symtab_raw.leak();
|
||||
Vec::from_raw_parts(symtab_ptr, cnt, cnt)
|
||||
};
|
||||
let origin = symtab.clone();
|
||||
symtab.sort_by_key(|x| x.st_value);
|
||||
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
me.symtab = Some(symtab);
|
||||
me.symtab_origin = Some(origin);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_strtab(&self) -> Result<(), Error> {
|
||||
{
|
||||
let me = self.backobj.borrow();
|
||||
|
||||
if me.strtab.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let sect_idx = if let Ok(idx) = self.find_section(".strtab") {
|
||||
idx
|
||||
} else {
|
||||
self.find_section(".dynstr")?
|
||||
};
|
||||
let strtab = self.read_section_raw(sect_idx)?;
|
||||
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
me.strtab = Some(strtab);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_str2symtab(&self) -> Result<(), Error> {
|
||||
self.ensure_symtab()?;
|
||||
self.ensure_strtab()?;
|
||||
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
if me.str2symtab.is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Build strtab offsets to symtab indices
|
||||
let strtab = me.strtab.as_ref().unwrap();
|
||||
let symtab = me.symtab.as_ref().unwrap();
|
||||
let mut str2symtab = Vec::<(usize, usize)>::with_capacity(symtab.len());
|
||||
for (sym_i, sym) in symtab.iter().enumerate() {
|
||||
let name_off = sym.st_name;
|
||||
str2symtab.push((name_off as usize, sym_i));
|
||||
}
|
||||
|
||||
// Sort in the dictionary order
|
||||
str2symtab
|
||||
.sort_by_key(|&x| unsafe { CStr::from_ptr(&strtab[x.0] as *const u8 as *const i8) });
|
||||
|
||||
me.str2symtab = Some(str2symtab);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_elf_file_type(&self) -> Result<u16, Error> {
|
||||
self.ensure_ehdr()?;
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
|
||||
Ok(me.ehdr.as_ref().unwrap().e_type)
|
||||
}
|
||||
|
||||
fn check_section_index(&self, sect_idx: usize) -> Result<(), Error> {
|
||||
let nsects = self.get_num_sections()?;
|
||||
|
||||
if nsects <= sect_idx {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "the index is too big"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn section_seek(&self, sect_idx: usize) -> Result<(), Error> {
|
||||
self.check_section_index(sect_idx)?;
|
||||
self.ensure_shdrs()?;
|
||||
let me = self.backobj.borrow();
|
||||
read_elf_section_seek(
|
||||
&mut self.file.borrow_mut(),
|
||||
&me.shdrs.as_ref().unwrap()[sect_idx],
|
||||
)
|
||||
}
|
||||
|
||||
/// Read the raw data of the section of a given index.
|
||||
pub fn read_section_raw(&self, sect_idx: usize) -> Result<Vec<u8>, Error> {
|
||||
self.check_section_index(sect_idx)?;
|
||||
self.ensure_shdrs()?;
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
read_elf_section_raw(
|
||||
&mut self.file.borrow_mut(),
|
||||
&me.shdrs.as_ref().unwrap()[sect_idx],
|
||||
)
|
||||
}
|
||||
|
||||
/// Read the raw data of the section of a given index.
|
||||
pub fn read_section_raw_cache(&self, sect_idx: usize) -> Result<&[u8], Error> {
|
||||
self.check_section_index(sect_idx)?;
|
||||
self.ensure_shdrs()?;
|
||||
|
||||
let mut me = self.backobj.borrow_mut();
|
||||
if me.sect_cache[sect_idx].is_none() {
|
||||
let buf = read_elf_section_raw(
|
||||
&mut self.file.borrow_mut(),
|
||||
&me.shdrs.as_ref().unwrap()[sect_idx],
|
||||
)?;
|
||||
me.sect_cache[sect_idx] = Some(buf);
|
||||
}
|
||||
|
||||
Ok(unsafe { mem::transmute(me.sect_cache[sect_idx].as_ref().unwrap().as_slice()) })
|
||||
}
|
||||
|
||||
/// Get the name of the section of a given index.
|
||||
pub fn get_section_name(&self, sect_idx: usize) -> Result<&str, Error> {
|
||||
self.check_section_index(sect_idx)?;
|
||||
|
||||
self.ensure_shstrtab()?;
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
|
||||
let sect = &me.shdrs.as_ref().unwrap()[sect_idx];
|
||||
let name = get_elf_section_name(sect, unsafe {
|
||||
(*self.backobj.as_ptr()).shstrtab.as_ref().unwrap()
|
||||
});
|
||||
if name.is_none() {
|
||||
return Err(Error::new(ErrorKind::InvalidData, "invalid section name"));
|
||||
}
|
||||
Ok(name.unwrap())
|
||||
}
|
||||
|
||||
pub fn get_section_size(&self, sect_idx: usize) -> Result<usize, Error> {
|
||||
self.check_section_index(sect_idx)?;
|
||||
self.ensure_shdrs()?;
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
let sect = &me.shdrs.as_ref().unwrap()[sect_idx];
|
||||
Ok(sect.sh_size as usize)
|
||||
}
|
||||
|
||||
pub fn get_num_sections(&self) -> Result<usize, Error> {
|
||||
self.ensure_ehdr()?;
|
||||
let me = self.backobj.borrow();
|
||||
Ok(me.ehdr.as_ref().unwrap().e_shnum as usize)
|
||||
}
|
||||
|
||||
/// Find the section of a given name.
|
||||
///
|
||||
/// This function return the index of the section if found.
|
||||
pub fn find_section(&self, name: &str) -> Result<usize, Error> {
|
||||
let nsects = self.get_num_sections()?;
|
||||
for i in 0..nsects {
|
||||
if self.get_section_name(i)? == name {
|
||||
return Ok(i);
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!("unable to find ELF section: {name}"),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn find_symbol(&self, address: u64, st_type: u8) -> Result<(&str, u64), Error> {
|
||||
self.ensure_symtab()?;
|
||||
self.ensure_strtab()?;
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
let idx_r =
|
||||
search_address_opt_key(me.symtab.as_ref().unwrap(), address, &|sym: &Elf64_Sym| {
|
||||
if sym.st_info & 0xf != st_type || sym.st_shndx == SHN_UNDEF {
|
||||
None
|
||||
} else {
|
||||
Some(sym.st_value)
|
||||
}
|
||||
});
|
||||
if idx_r.is_none() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::NotFound,
|
||||
"Does not found a symbol for the given address",
|
||||
));
|
||||
}
|
||||
let idx = idx_r.unwrap();
|
||||
|
||||
let sym = &me.symtab.as_ref().unwrap()[idx];
|
||||
let sym_name = match extract_string(
|
||||
unsafe { (*self.backobj.as_ptr()).strtab.as_ref().unwrap().as_slice() },
|
||||
sym.st_name as usize,
|
||||
) {
|
||||
Some(sym_name) => sym_name,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"invalid symbol name string/offset",
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok((sym_name, sym.st_value))
|
||||
}
|
||||
|
||||
pub fn find_address(&self, name: &str, opts: &FindAddrOpts) -> Result<Vec<SymbolInfo>, Error> {
|
||||
if let SymbolType::Variable = opts.sym_type {
|
||||
return Err(Error::new(ErrorKind::Unsupported, "Not implemented"));
|
||||
}
|
||||
|
||||
self.ensure_str2symtab()?;
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
let str2symtab = me.str2symtab.as_ref().unwrap();
|
||||
let strtab = me.strtab.as_ref().unwrap();
|
||||
let r = str2symtab.binary_search_by_key(&name.to_string(), |&x| {
|
||||
String::from(
|
||||
unsafe { CStr::from_ptr(&strtab[x.0] as *const u8 as *const i8) }
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)
|
||||
});
|
||||
|
||||
match r {
|
||||
Ok(str2sym_i) => {
|
||||
let mut idx = str2sym_i;
|
||||
while idx > 0 {
|
||||
let name_seek = unsafe {
|
||||
CStr::from_ptr(&strtab[str2symtab[idx].0] as *const u8 as *const i8)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
};
|
||||
if !name_seek.eq(name) {
|
||||
idx += 1;
|
||||
break;
|
||||
}
|
||||
idx -= 1;
|
||||
}
|
||||
|
||||
let mut found = vec![];
|
||||
for idx in idx..str2symtab.len() {
|
||||
let name_visit = unsafe {
|
||||
CStr::from_ptr(&strtab[str2symtab[idx].0] as *const u8 as *const i8)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
};
|
||||
if !name_visit.eq(name) {
|
||||
break;
|
||||
}
|
||||
let sym_i = str2symtab[idx].1;
|
||||
let sym_ref = &me.symtab.as_ref().unwrap()[sym_i];
|
||||
if sym_ref.st_shndx != SHN_UNDEF {
|
||||
found.push(SymbolInfo {
|
||||
name: name.to_string(),
|
||||
address: sym_ref.st_value,
|
||||
size: sym_ref.st_size,
|
||||
sym_type: SymbolType::Function,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(found)
|
||||
}
|
||||
Err(_) => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_address_regex(
|
||||
&self,
|
||||
pattern: &str,
|
||||
opts: &FindAddrOpts,
|
||||
) -> Result<Vec<SymbolInfo>, Error> {
|
||||
if let SymbolType::Variable = opts.sym_type {
|
||||
return Err(Error::new(ErrorKind::Unsupported, "Not implemented"));
|
||||
}
|
||||
|
||||
self.ensure_str2symtab()?;
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
let str2symtab = me.str2symtab.as_ref().unwrap();
|
||||
let strtab = me.strtab.as_ref().unwrap();
|
||||
let re = Regex::new(pattern).unwrap();
|
||||
let mut syms = vec![];
|
||||
for (str_off, sym_i) in str2symtab {
|
||||
let sname = unsafe {
|
||||
CStr::from_ptr(&strtab[*str_off] as *const u8 as *const i8)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
};
|
||||
if re.is_match(sname) {
|
||||
let sym_ref = &me.symtab.as_ref().unwrap()[*sym_i];
|
||||
if sym_ref.st_shndx != SHN_UNDEF {
|
||||
syms.push(SymbolInfo {
|
||||
name: sname.to_string(),
|
||||
address: sym_ref.st_value,
|
||||
size: sym_ref.st_size,
|
||||
sym_type: SymbolType::Function,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(syms)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn get_symbol(&self, idx: usize) -> Result<&Elf64_Sym, Error> {
|
||||
self.ensure_symtab()?;
|
||||
|
||||
let me = self.backobj.as_ptr();
|
||||
Ok(unsafe { &(*me).symtab.as_mut().unwrap()[idx] })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn get_symbol_name(&self, idx: usize) -> Result<&str, Error> {
|
||||
let sym = self.get_symbol(idx)?;
|
||||
|
||||
let me = self.backobj.as_ptr();
|
||||
let sym_name = match extract_string(
|
||||
unsafe { (*me).strtab.as_ref().unwrap().as_slice() },
|
||||
sym.st_name as usize,
|
||||
) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"invalid symb name string/offset",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(sym_name)
|
||||
}
|
||||
|
||||
pub fn get_all_program_headers(&self) -> Result<&[Elf64_Phdr], Error> {
|
||||
self.ensure_phdrs()?;
|
||||
|
||||
let phdrs = unsafe {
|
||||
let me = self.backobj.as_ptr();
|
||||
let phdrs_ref = (*me).phdrs.as_mut().unwrap();
|
||||
phdrs_ref
|
||||
};
|
||||
Ok(phdrs)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn pick_symtab_addr(&self) -> (&str, u64) {
|
||||
self.ensure_symtab().unwrap();
|
||||
self.ensure_strtab().unwrap();
|
||||
|
||||
let me = self.backobj.borrow();
|
||||
let symtab = me.symtab.as_ref().unwrap();
|
||||
let mut idx = symtab.len() / 2;
|
||||
while symtab[idx].st_info & 0xf != STT_FUNC || symtab[idx].st_shndx == SHN_UNDEF {
|
||||
idx += 1;
|
||||
}
|
||||
let sym = &symtab[idx];
|
||||
let addr = sym.st_value;
|
||||
drop(me);
|
||||
|
||||
let sym_name = self.get_symbol_name(idx).unwrap();
|
||||
(sym_name, addr)
|
||||
}
|
||||
|
||||
/// Read raw data from the file at the current position.
|
||||
///
|
||||
/// The caller can use section_seek() to move the current position
|
||||
/// of the backed file. However, this function doesn't promise to
|
||||
/// not cross the boundary of the section. The caller should take
|
||||
/// care about it.
|
||||
pub unsafe fn read_raw(&self, buf: &mut [u8]) -> Result<(), Error> {
|
||||
self.file.borrow_mut().read_exact(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::env;
|
||||
|
||||
#[test]
|
||||
fn test_elf_header_sections() {
|
||||
let bin_name = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test-no-debug.bin");
|
||||
|
||||
let mut bin_file = File::open(bin_name).unwrap();
|
||||
let ehdr = read_elf_header(&mut bin_file);
|
||||
assert!(ehdr.is_ok());
|
||||
let ehdr = ehdr.unwrap();
|
||||
assert_eq!(
|
||||
ehdr.e_ident,
|
||||
[
|
||||
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
||||
]
|
||||
);
|
||||
assert_eq!(ehdr.e_version, 0x1);
|
||||
assert_eq!(ehdr.e_shentsize as usize, mem::size_of::<Elf64_Shdr>());
|
||||
|
||||
let shdrs = read_elf_sections(&mut bin_file, &ehdr);
|
||||
assert!(shdrs.is_ok());
|
||||
let shdrs = shdrs.unwrap();
|
||||
let shstrndx = ehdr.e_shstrndx as usize;
|
||||
|
||||
let shstrtab_sec = &shdrs[shstrndx];
|
||||
let shstrtab = read_elf_section_raw(&mut bin_file, shstrtab_sec);
|
||||
assert!(shstrtab.is_ok());
|
||||
let shstrtab = shstrtab.unwrap();
|
||||
|
||||
let sec_name = get_elf_section_name(shstrtab_sec, &shstrtab);
|
||||
assert!(sec_name.is_some());
|
||||
assert_eq!(sec_name.unwrap(), ".shstrtab");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elf64_parser() {
|
||||
let bin_name = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test-no-debug.bin");
|
||||
|
||||
let parser = ElfParser::open(bin_name.as_ref()).unwrap();
|
||||
assert!(parser.find_section(".shstrtab").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elf64_symtab() {
|
||||
let bin_name = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test-no-debug.bin");
|
||||
|
||||
let parser = ElfParser::open(bin_name.as_ref()).unwrap();
|
||||
assert!(parser.find_section(".shstrtab").is_ok());
|
||||
|
||||
let (sym_name, addr) = parser.pick_symtab_addr();
|
||||
|
||||
let sym_r = parser.find_symbol(addr, STT_FUNC);
|
||||
assert!(sym_r.is_ok());
|
||||
let (sym_name_ret, addr_ret) = sym_r.unwrap();
|
||||
assert_eq!(addr_ret, addr);
|
||||
assert_eq!(sym_name_ret, sym_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elf64_find_address() {
|
||||
let bin_name = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test-no-debug.bin");
|
||||
|
||||
let parser = ElfParser::open(bin_name.as_ref()).unwrap();
|
||||
assert!(parser.find_section(".shstrtab").is_ok());
|
||||
|
||||
let (sym_name, addr) = parser.pick_symtab_addr();
|
||||
|
||||
println!("{sym_name}");
|
||||
let opts = FindAddrOpts {
|
||||
offset_in_file: false,
|
||||
obj_file_name: false,
|
||||
sym_type: SymbolType::Unknown,
|
||||
};
|
||||
let addr_r = parser.find_address(sym_name, &opts).unwrap();
|
||||
assert_eq!(addr_r.len(), 1);
|
||||
assert!(addr_r.iter().any(|x| x.address == addr));
|
||||
}
|
||||
}
|
||||
186
third_party/blazesym/src/elf/resolver.rs
vendored
Normal file
186
third_party/blazesym/src/elf/resolver.rs
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::AddressLineInfo;
|
||||
use crate::CacheHolder;
|
||||
use crate::Error;
|
||||
use crate::FindAddrOpts;
|
||||
use crate::SymResolver;
|
||||
use crate::SymbolInfo;
|
||||
|
||||
use super::cache::ElfBackend;
|
||||
use super::types::ET_DYN;
|
||||
use super::types::ET_EXEC;
|
||||
use super::types::PF_X;
|
||||
use super::types::PT_LOAD;
|
||||
use super::types::STT_FUNC;
|
||||
use super::ElfParser;
|
||||
|
||||
/// The symbol resolver for a single ELF file.
|
||||
///
|
||||
/// An ELF file may be loaded into an address space with a relocation.
|
||||
/// The callers should provide the path of an ELF file and where it's
|
||||
/// executable segment(s) is loaded.
|
||||
///
|
||||
/// For some ELF files, they are located at a specific address
|
||||
/// determined during compile-time. For these cases, just pass `0` as
|
||||
/// it's loaded address.
|
||||
pub struct ElfResolver {
|
||||
backend: ElfBackend,
|
||||
loaded_address: u64,
|
||||
loaded_to_virt: u64,
|
||||
foff_to_virt: u64,
|
||||
size: u64,
|
||||
file_name: PathBuf,
|
||||
}
|
||||
|
||||
impl ElfResolver {
|
||||
pub(crate) fn new(
|
||||
file_name: &Path,
|
||||
loaded_address: u64,
|
||||
cache_holder: &CacheHolder,
|
||||
) -> Result<ElfResolver, Error> {
|
||||
let backend = cache_holder.get_elf_cache().find(file_name)?;
|
||||
let parser = match &backend {
|
||||
ElfBackend::Dwarf(dwarf) => dwarf.get_parser(),
|
||||
ElfBackend::Elf(parser) => parser,
|
||||
};
|
||||
let e_type = parser.get_elf_file_type()?;
|
||||
let phdrs = parser.get_all_program_headers()?;
|
||||
|
||||
// Find the size of the block where the ELF file is/was
|
||||
// mapped.
|
||||
let mut max_addr = 0;
|
||||
let mut low_addr = 0xffffffffffffffff;
|
||||
let mut low_off = 0xffffffffffffffff;
|
||||
if e_type == ET_DYN || e_type == ET_EXEC {
|
||||
for phdr in phdrs {
|
||||
if phdr.p_type != PT_LOAD {
|
||||
continue;
|
||||
}
|
||||
if (phdr.p_flags & PF_X) != PF_X {
|
||||
continue;
|
||||
}
|
||||
let end_at = phdr.p_vaddr + phdr.p_memsz;
|
||||
if max_addr < end_at {
|
||||
max_addr = end_at;
|
||||
}
|
||||
if phdr.p_vaddr < low_addr {
|
||||
low_addr = phdr.p_vaddr;
|
||||
low_off = phdr.p_offset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(ErrorKind::InvalidData, "unknown e_type"));
|
||||
}
|
||||
|
||||
let loaded_address = if e_type == ET_EXEC {
|
||||
low_addr
|
||||
} else {
|
||||
loaded_address
|
||||
};
|
||||
let loaded_to_virt = low_addr;
|
||||
let foff_to_virt = low_addr - low_off;
|
||||
let size = max_addr - low_addr;
|
||||
|
||||
Ok(ElfResolver {
|
||||
backend,
|
||||
loaded_address,
|
||||
loaded_to_virt,
|
||||
foff_to_virt,
|
||||
size,
|
||||
file_name: file_name.to_path_buf(),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_parser(&self) -> Option<&ElfParser> {
|
||||
match &self.backend {
|
||||
ElfBackend::Dwarf(dwarf) => Some(dwarf.get_parser()),
|
||||
ElfBackend::Elf(parser) => Some(parser),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymResolver for ElfResolver {
|
||||
fn get_address_range(&self) -> (u64, u64) {
|
||||
(self.loaded_address, self.loaded_address + self.size)
|
||||
}
|
||||
|
||||
fn find_symbols(&self, addr: u64) -> Vec<(&str, u64)> {
|
||||
let off = addr - self.loaded_address + self.loaded_to_virt;
|
||||
let parser = if let Some(parser) = self.get_parser() {
|
||||
parser
|
||||
} else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
match parser.find_symbol(off, STT_FUNC) {
|
||||
Ok((name, start_addr)) => {
|
||||
vec![(name, start_addr - self.loaded_to_virt + self.loaded_address)]
|
||||
}
|
||||
Err(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn find_address(&self, name: &str, opts: &FindAddrOpts) -> Option<Vec<SymbolInfo>> {
|
||||
let mut addr_res = match &self.backend {
|
||||
ElfBackend::Dwarf(dwarf) => dwarf.find_address(name, opts),
|
||||
ElfBackend::Elf(parser) => parser.find_address(name, opts),
|
||||
}
|
||||
.ok()?;
|
||||
for x in &mut addr_res {
|
||||
x.address = x.address - self.loaded_to_virt + self.loaded_address;
|
||||
}
|
||||
Some(addr_res)
|
||||
}
|
||||
|
||||
fn find_address_regex(&self, pattern: &str, opts: &FindAddrOpts) -> Option<Vec<SymbolInfo>> {
|
||||
let syms = match &self.backend {
|
||||
ElfBackend::Dwarf(dwarf) => dwarf.find_address_regex(pattern, opts),
|
||||
ElfBackend::Elf(parser) => parser.find_address_regex(pattern, opts),
|
||||
};
|
||||
if syms.is_err() {
|
||||
return None;
|
||||
}
|
||||
let mut syms = syms.unwrap();
|
||||
for sym in &mut syms {
|
||||
sym.address = sym.address - self.loaded_to_virt + self.loaded_address;
|
||||
}
|
||||
Some(syms)
|
||||
}
|
||||
|
||||
fn find_line_info(&self, addr: u64) -> Option<AddressLineInfo> {
|
||||
let off = addr - self.loaded_address + self.loaded_to_virt;
|
||||
if let ElfBackend::Dwarf(dwarf) = &self.backend {
|
||||
let (directory, file, line_no) = dwarf.find_line_as_ref(off)?;
|
||||
let mut path = String::from(directory);
|
||||
if !path.is_empty() && &path[(path.len() - 1)..] != "/" {
|
||||
path.push('/');
|
||||
}
|
||||
path.push_str(file);
|
||||
Some(AddressLineInfo {
|
||||
path,
|
||||
line_no,
|
||||
column: 0,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn addr_file_off(&self, addr: u64) -> Option<u64> {
|
||||
Some(addr - self.loaded_address + self.loaded_to_virt - self.foff_to_virt)
|
||||
}
|
||||
|
||||
fn get_obj_file_name(&self) -> &Path {
|
||||
&self.file_name
|
||||
}
|
||||
|
||||
fn repr(&self) -> String {
|
||||
match self.backend {
|
||||
ElfBackend::Dwarf(_) => format!("DWARF {}", self.file_name.display()),
|
||||
ElfBackend::Elf(_) => format!("ELF {}", self.file_name.display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
41
third_party/blazesym/src/elf/types.rs
vendored
Normal file
41
third_party/blazesym/src/elf/types.rs
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
pub use libc::Elf64_Addr;
|
||||
pub use libc::Elf64_Half;
|
||||
pub use libc::Elf64_Off;
|
||||
pub use libc::Elf64_Phdr;
|
||||
pub use libc::Elf64_Shdr;
|
||||
pub use libc::Elf64_Sxword;
|
||||
pub use libc::Elf64_Sym;
|
||||
pub use libc::Elf64_Word;
|
||||
pub use libc::Elf64_Xword;
|
||||
|
||||
pub use libc::Elf64_Ehdr;
|
||||
pub use libc::ET_CORE;
|
||||
pub use libc::ET_DYN;
|
||||
pub use libc::ET_EXEC;
|
||||
pub use libc::ET_HIPROC;
|
||||
pub use libc::ET_LOPROC;
|
||||
pub use libc::ET_NONE;
|
||||
pub use libc::ET_REL;
|
||||
|
||||
pub use libc::PF_R;
|
||||
pub use libc::PF_W;
|
||||
pub use libc::PF_X;
|
||||
|
||||
pub use libc::PT_DYNAMIC;
|
||||
pub use libc::PT_GNU_EH_FRAME;
|
||||
pub use libc::PT_GNU_STACK;
|
||||
pub use libc::PT_HIOS;
|
||||
pub use libc::PT_HIPROC;
|
||||
pub use libc::PT_INTERP;
|
||||
pub use libc::PT_LOAD;
|
||||
pub use libc::PT_LOOS;
|
||||
pub use libc::PT_LOPROC;
|
||||
pub use libc::PT_NOTE;
|
||||
pub use libc::PT_NULL;
|
||||
pub use libc::PT_PHDR;
|
||||
pub use libc::PT_SHLIB;
|
||||
pub use libc::PT_TLS;
|
||||
|
||||
pub const SHN_UNDEF: u16 = 0;
|
||||
|
||||
pub const STT_FUNC: u8 = 2;
|
||||
147
third_party/blazesym/src/gsym/linetab.rs
vendored
Normal file
147
third_party/blazesym/src/gsym/linetab.rs
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
//! Opcode runner of GSYM line table.
|
||||
|
||||
use crate::util::decode_leb128;
|
||||
use crate::util::decode_leb128_s;
|
||||
|
||||
/// End of the line table
|
||||
const END_SEQUENCE: u8 = 0x00;
|
||||
/// Set [`LineTableRow.file_idx`], don't push a row.
|
||||
const SET_FILE: u8 = 0x01;
|
||||
/// Increment [`LineTableRow.address`], and push a row.
|
||||
const ADVANCE_PC: u8 = 0x02;
|
||||
/// Set [`LineTableRow.file_line`], don't push a row.
|
||||
const ADVANCE_LINE: u8 = 0x03;
|
||||
/// All special opcodes push a row.
|
||||
const FIRST_SPECIAL: u8 = 0x04;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RunResult {
|
||||
/// Run the operator successfully.
|
||||
Ok(usize),
|
||||
/// This operator creates a new row.
|
||||
NewRow(usize),
|
||||
/// The end of the program (the operator stream.)
|
||||
End,
|
||||
/// Fails to run the operator at the position.
|
||||
Err,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LineTableHeader {
|
||||
/// `min_data` & `max_delta` together is used to set the range and encoding
|
||||
/// of line delta in special operator. Line delta is the number of lines
|
||||
/// that a line table row is different from the previous row.
|
||||
pub min_delta: i64,
|
||||
pub max_delta: i64,
|
||||
pub first_line: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LineTableRow {
|
||||
pub address: u64,
|
||||
pub file_idx: u32,
|
||||
pub file_line: u32,
|
||||
}
|
||||
|
||||
impl LineTableRow {
|
||||
/// Create a `LineTableRow` to use as the states of a line table virtual
|
||||
/// machine.
|
||||
///
|
||||
/// The returned `LineTableRow` can be passed to [`run_op`] as `ctx`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `header` - is a [`LineTableHeader`] returned by [`parse_line_table_header()`].
|
||||
/// * `symaddr` - the address of the symbol that `header` belongs to.
|
||||
pub fn line_table_row_from(header: &LineTableHeader, symaddr: u64) -> LineTableRow {
|
||||
Self {
|
||||
address: symaddr,
|
||||
file_idx: 1,
|
||||
file_line: header.first_line,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Run a GSYM line table operator/instruction in the buffer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `ctx` - a line table row to present the current states of the virtual
|
||||
/// machine. [`line_table_row_from()`] can create a `LineTableRow` to
|
||||
/// keep the states of a virtual machine.
|
||||
/// * `header` - is a `LineTableHeader`.
|
||||
/// * `ops` - is the buffer of the operators following the `LineTableHeader` in
|
||||
/// a GSYM file.
|
||||
/// * `pc` - is the program counter of the virtual machine.
|
||||
///
|
||||
/// Returns a [`RunResult`]. `Ok` and `NewRow` will return the size of this
|
||||
/// instruction. The caller should adjust the value of `pc` according to the
|
||||
/// value returned.
|
||||
pub fn run_op(
|
||||
ctx: &mut LineTableRow,
|
||||
header: &LineTableHeader,
|
||||
ops: &[u8],
|
||||
pc: usize,
|
||||
) -> RunResult {
|
||||
let mut off = pc;
|
||||
let op = ops[off];
|
||||
off += 1;
|
||||
match op {
|
||||
END_SEQUENCE => RunResult::End,
|
||||
SET_FILE => {
|
||||
if let Some((f, bytes)) = decode_leb128(&ops[off..]) {
|
||||
off += bytes as usize;
|
||||
ctx.file_idx = f as u32;
|
||||
RunResult::Ok(off - pc)
|
||||
} else {
|
||||
RunResult::Err
|
||||
}
|
||||
}
|
||||
ADVANCE_PC => {
|
||||
if let Some((adv, bytes)) = decode_leb128(&ops[off..]) {
|
||||
off += bytes as usize;
|
||||
ctx.address += adv;
|
||||
RunResult::NewRow(off - pc)
|
||||
} else {
|
||||
RunResult::Err
|
||||
}
|
||||
}
|
||||
ADVANCE_LINE => {
|
||||
if let Some((adv, bytes)) = decode_leb128_s(&ops[off..]) {
|
||||
off += bytes as usize;
|
||||
ctx.file_line = (ctx.file_line as i64 + adv) as u32;
|
||||
RunResult::Ok(off - pc)
|
||||
} else {
|
||||
RunResult::Err
|
||||
}
|
||||
}
|
||||
// Special operators.
|
||||
//
|
||||
// All operators that have a value greater than or equal to
|
||||
// FIRST_SPECIAL are considered special operators. These operators
|
||||
// change both the line number and address of the virtual machine and
|
||||
// emit a new row.
|
||||
_ => {
|
||||
let adjusted = (op - FIRST_SPECIAL) as i64;
|
||||
// The range of line number delta is from min_delta to max_delta,
|
||||
// including max_delta.
|
||||
let range = header.max_delta - header.min_delta + 1;
|
||||
if range == 0 {
|
||||
return RunResult::Err;
|
||||
}
|
||||
let line_delta = header.min_delta + (adjusted % range);
|
||||
let addr_delta = adjusted / range;
|
||||
|
||||
let file_line = ctx.file_line as i32 + line_delta as i32;
|
||||
if file_line < 1 {
|
||||
return RunResult::Err;
|
||||
}
|
||||
|
||||
ctx.file_line = file_line as u32;
|
||||
ctx.address = (ctx.address as i64 + addr_delta) as u64;
|
||||
RunResult::NewRow(off - pc)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
third_party/blazesym/src/gsym/mod.rs
vendored
Normal file
6
third_party/blazesym/src/gsym/mod.rs
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
mod linetab;
|
||||
mod parser;
|
||||
mod resolver;
|
||||
mod types;
|
||||
|
||||
pub use resolver::GsymResolver;
|
||||
469
third_party/blazesym/src/gsym/parser.rs
vendored
Normal file
469
third_party/blazesym/src/gsym/parser.rs
vendored
Normal file
@@ -0,0 +1,469 @@
|
||||
//! Parser of GSYM format.
|
||||
//!
|
||||
//! The layout of a standalone GSYM contains following sections in the order.
|
||||
//!
|
||||
//! * Header
|
||||
//! * Address Table
|
||||
//! * Address Data Offset Table
|
||||
//! * File Table
|
||||
//! * String Table
|
||||
//! * Address Data
|
||||
//!
|
||||
//! The standalone GSYM starts with a Header, which describes the
|
||||
//! size of an entry in the address table, the number of entries in
|
||||
//! the address table, and the location and the size of the string
|
||||
//! table.
|
||||
//!
|
||||
//! Since the Address Table is immediately after the Header, the
|
||||
//! Header describes only the size of an entry and number of entries
|
||||
//! in the table but not where it is. The Address Table comprises
|
||||
//! addresses of symbols in the ascending order, so we can find the
|
||||
//! symbol an address belonging to by doing a binary search to find
|
||||
//! the most close address but smaller or equal.
|
||||
//!
|
||||
//! The Address Data Offset Table has the same number of entries as
|
||||
//! the Address Table. Every entry in one table will has
|
||||
//! corresponding entry at the same offset in the other table. The
|
||||
//! entries in the Address Data Offset Table are always 32bits
|
||||
//! (4bytes.) It is the file offset to the respective Address
|
||||
//! Data. (AddressInfo actually)
|
||||
//!
|
||||
//! An AddressInfo comprises the size and name of a symbol. The name
|
||||
//! is an offset in the string table. You will find a null terminated
|
||||
//! C string at the give offset. The size is the number of bytes of
|
||||
//! the respective object; ex, a function or variable.
|
||||
//!
|
||||
//! See <https://reviews.llvm.org/D53379>
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
use crate::util::decode_leb128;
|
||||
use crate::util::decode_leb128_s;
|
||||
use crate::util::decode_udword;
|
||||
use crate::util::decode_uhalf;
|
||||
use crate::util::decode_uword;
|
||||
|
||||
use super::linetab::LineTableHeader;
|
||||
use super::types::AddressData;
|
||||
use super::types::AddressInfo;
|
||||
use super::types::FileInfo;
|
||||
use super::types::Header;
|
||||
use super::types::InfoTypeEndOfList;
|
||||
use super::types::InfoTypeInlineInfo;
|
||||
use super::types::InfoTypeLineTableInfo;
|
||||
use super::types::ADDR_DATA_OFFSET_SIZE;
|
||||
use super::types::FILE_INFO_SIZE;
|
||||
use super::types::GSYM_MAGIC;
|
||||
use super::types::GSYM_VERSION;
|
||||
|
||||
/// Hold the major parts of a standalone GSYM file.
|
||||
///
|
||||
/// GsymContext provides functions to access major entities in GSYM.
|
||||
/// GsymContext can find respective AddressInfo for an address. But,
|
||||
/// it doesn't parse AddressData to get line numbers.
|
||||
///
|
||||
/// The developers should use [`parse_address_data()`],
|
||||
/// [`parse_line_table_header()`], and [`linetab::run_op()`] to get
|
||||
/// line number information from [`AddressInfo`].
|
||||
pub struct GsymContext<'a> {
|
||||
header: Header,
|
||||
addr_tab: &'a [u8],
|
||||
addr_data_off_tab: &'a [u8],
|
||||
file_tab: &'a [u8],
|
||||
str_tab: &'a [u8],
|
||||
raw_data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> GsymContext<'a> {
|
||||
/// Parse the Header of a standalone GSYM file.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data` - is the content of a standalone GSYM.
|
||||
///
|
||||
/// Returns a GsymContext, which includes the Header and other important tables.
|
||||
pub fn parse_header(data: &[u8]) -> Result<GsymContext, Error> {
|
||||
let mut off = 0;
|
||||
// Parse Header
|
||||
let magic = decode_uword(data);
|
||||
if magic != GSYM_MAGIC {
|
||||
return Err(Error::new(ErrorKind::InvalidData, "invalid magic number"));
|
||||
}
|
||||
off += 4;
|
||||
let version = decode_uhalf(&data[off..]);
|
||||
if version != GSYM_VERSION {
|
||||
return Err(Error::new(ErrorKind::InvalidData, "unknown version number"));
|
||||
}
|
||||
off += 2;
|
||||
let addr_off_size = data[off];
|
||||
off += 1;
|
||||
let uuid_size = data[off];
|
||||
off += 1;
|
||||
let base_address = decode_udword(&data[off..]);
|
||||
off += 8;
|
||||
let num_addrs = decode_uword(&data[off..]);
|
||||
off += 4;
|
||||
let strtab_offset = decode_uword(&data[off..]);
|
||||
off += 4;
|
||||
let strtab_size = decode_uword(&data[off..]);
|
||||
off += 4;
|
||||
let uuid: [u8; 20] = (&data[off..(off + 20)])
|
||||
.try_into()
|
||||
.expect("input data is too short");
|
||||
off += 20;
|
||||
|
||||
// Get the slices of the Address Table, Address Data Offset Table,
|
||||
// and String table.
|
||||
let end_off = off + num_addrs as usize * addr_off_size as usize;
|
||||
if end_off > data.len() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"the size of the file is smaller than expectation (address table)",
|
||||
));
|
||||
}
|
||||
let addr_tab = &data[off..end_off];
|
||||
off = (end_off + 0x3) & !0x3;
|
||||
let end_off = off + num_addrs as usize * ADDR_DATA_OFFSET_SIZE;
|
||||
if end_off > data.len() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"the size of the file is smaller than expectation (address data offset table)",
|
||||
));
|
||||
}
|
||||
let addr_data_off_tab = &data[off..end_off];
|
||||
off += num_addrs as usize * ADDR_DATA_OFFSET_SIZE;
|
||||
let file_num = decode_uword(&data[off..]);
|
||||
off += 4;
|
||||
let end_off = off + file_num as usize * FILE_INFO_SIZE;
|
||||
if end_off > data.len() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"the size of the file is smaller than expectation (file table)",
|
||||
));
|
||||
}
|
||||
let file_tab = &data[off..end_off];
|
||||
let end_off = strtab_offset as usize + strtab_size as usize;
|
||||
if end_off > data.len() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"the size of the file is smaller than expectation (string table)",
|
||||
));
|
||||
}
|
||||
let str_tab = &data[strtab_offset as usize..end_off];
|
||||
|
||||
Ok(GsymContext {
|
||||
header: Header {
|
||||
magic,
|
||||
version,
|
||||
addr_off_size,
|
||||
uuid_size,
|
||||
base_address,
|
||||
num_addrs,
|
||||
strtab_offset,
|
||||
strtab_size,
|
||||
uuid,
|
||||
},
|
||||
addr_tab,
|
||||
addr_data_off_tab,
|
||||
file_tab,
|
||||
str_tab,
|
||||
raw_data: data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn num_addresses(&self) -> usize {
|
||||
self.header.num_addrs as usize
|
||||
}
|
||||
|
||||
/// Get the address of an entry in the Address Table.
|
||||
pub fn addr_at(&self, idx: usize) -> Option<u64> {
|
||||
if idx >= self.header.num_addrs as usize {
|
||||
return None;
|
||||
}
|
||||
|
||||
let off = idx * self.header.addr_off_size as usize;
|
||||
let mut addr = 0u64;
|
||||
let mut shift = 0;
|
||||
for d in &self.addr_tab[off..(off + self.header.addr_off_size as usize)] {
|
||||
addr |= (*d as u64) << shift;
|
||||
shift += 8;
|
||||
}
|
||||
addr += self.header.base_address;
|
||||
Some(addr)
|
||||
}
|
||||
|
||||
/// Get the AddressInfo of an address given by an index.
|
||||
pub fn addr_info(&self, idx: usize) -> Option<AddressInfo> {
|
||||
if idx >= self.header.num_addrs as usize {
|
||||
return None;
|
||||
}
|
||||
|
||||
let off = idx * ADDR_DATA_OFFSET_SIZE;
|
||||
let ad_off = decode_uword(&self.addr_data_off_tab[off..]) as usize;
|
||||
let size = decode_uword(&self.raw_data[ad_off..]);
|
||||
let name = decode_uword(&self.raw_data[ad_off + 4..]);
|
||||
let info = AddressInfo {
|
||||
size,
|
||||
name,
|
||||
data: &self.raw_data[ad_off + 8..],
|
||||
};
|
||||
|
||||
Some(info)
|
||||
}
|
||||
|
||||
/// Get the string at the given offset from the String Table.
|
||||
pub fn get_str(&self, off: usize) -> Option<&str> {
|
||||
if off >= self.str_tab.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Ensure there is a null byte.
|
||||
let mut null_off = self.str_tab.len() - 1;
|
||||
while null_off > off && self.str_tab[null_off] != 0 {
|
||||
null_off -= 1;
|
||||
}
|
||||
if null_off == off {
|
||||
return Some("");
|
||||
}
|
||||
|
||||
// SAFETY: the lifetime of `CStr` can live as long as `self`.
|
||||
// The returned reference can also live as long as `self`.
|
||||
unsafe {
|
||||
CStr::from_ptr(self.str_tab[off..].as_ptr() as *const i8)
|
||||
.to_str()
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_info(&self, idx: usize) -> Option<FileInfo> {
|
||||
if idx >= self.file_tab.len() / FILE_INFO_SIZE {
|
||||
return None;
|
||||
}
|
||||
let mut off = idx * FILE_INFO_SIZE;
|
||||
let directory = decode_uword(&self.file_tab[off..(off + 4)]);
|
||||
off += 4;
|
||||
let filename = decode_uword(&self.file_tab[off..(off + 4)]);
|
||||
let info = FileInfo {
|
||||
directory,
|
||||
filename,
|
||||
};
|
||||
Some(info)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the index of an entry in the address table most likely
|
||||
/// containing the given address.
|
||||
///
|
||||
/// The callers should check the respective `AddressInfo` to make sure
|
||||
/// it is what they request for.
|
||||
pub fn find_address(ctx: &GsymContext, addr: u64) -> Option<usize> {
|
||||
let mut left = 0;
|
||||
let mut right = ctx.num_addresses();
|
||||
|
||||
if right == 0 {
|
||||
return None;
|
||||
}
|
||||
if addr < ctx.addr_at(0)? {
|
||||
return None;
|
||||
}
|
||||
|
||||
while (left + 1) < right {
|
||||
let v = (left + right) / 2;
|
||||
let cur_addr = ctx.addr_at(v)?;
|
||||
|
||||
if addr == cur_addr {
|
||||
return Some(v);
|
||||
}
|
||||
if addr < cur_addr {
|
||||
right = v;
|
||||
} else {
|
||||
left = v;
|
||||
}
|
||||
}
|
||||
Some(left)
|
||||
}
|
||||
|
||||
/// Parse AddressData.
|
||||
///
|
||||
/// AddressDatas are items following AndressInfo.
|
||||
/// [`GsymContext::addr_info()`] returns the raw data of AddressDatas as a
|
||||
/// slice at [`AddressInfo::data`].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data` - is the slice from AddressInfo::data.
|
||||
///
|
||||
/// Returns a vector of [`AddressData`].
|
||||
pub fn parse_address_data(data: &[u8]) -> Vec<AddressData> {
|
||||
let mut data_objs = vec![];
|
||||
|
||||
let mut off = 0;
|
||||
while off < data.len() {
|
||||
let typ = decode_uword(&data[off..]);
|
||||
off += 4;
|
||||
let length = decode_uword(&data[off..]);
|
||||
off += 4;
|
||||
let d = &data[off..(off + length as usize)];
|
||||
data_objs.push(AddressData {
|
||||
typ,
|
||||
length,
|
||||
data: d,
|
||||
});
|
||||
off += length as usize;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
match typ {
|
||||
InfoTypeEndOfList => {
|
||||
break;
|
||||
}
|
||||
InfoTypeLineTableInfo | InfoTypeInlineInfo => {}
|
||||
_ => {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("unknown info type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_objs
|
||||
}
|
||||
|
||||
/// Parse AddressData of InfoTypeLineTableInfo.
|
||||
///
|
||||
/// An `AddressData` of `InfoTypeLineTableInfo` type is a table of line numbers
|
||||
/// for a symbol. `AddressData` is the payload of `AddressInfo`. One
|
||||
/// `AddressInfo` may have several `AddressData` entries in its payload. Each
|
||||
/// `AddressData` entry stores a type of data relates to the symbol the
|
||||
/// `AddressInfo` presents.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data` - is what [`AddressData::data`] is.
|
||||
///
|
||||
/// Returns the `LineTableHeader` and the size of the header of a
|
||||
/// `AddressData` entry of `InfoTypeLineTableInfo` type in the payload
|
||||
/// of an `Addressinfo`.
|
||||
pub fn parse_line_table_header(data: &[u8]) -> Option<(LineTableHeader, usize)> {
|
||||
let mut off = 0;
|
||||
let (min_delta, bytes) = decode_leb128_s(&data[off..])?;
|
||||
off += bytes as usize;
|
||||
let (max_delta, bytes) = decode_leb128_s(&data[off..])?;
|
||||
off += bytes as usize;
|
||||
let (first_line, bytes) = decode_leb128(&data[off..])?;
|
||||
off += bytes as usize;
|
||||
|
||||
let header = LineTableHeader {
|
||||
min_delta,
|
||||
max_delta,
|
||||
first_line: first_line as u32,
|
||||
};
|
||||
Some((header, off))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_context() {
|
||||
let test_gsym = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test.gsym");
|
||||
let mut gsym_fo = File::open(test_gsym).unwrap();
|
||||
let mut data = vec![];
|
||||
|
||||
gsym_fo.read_to_end(&mut data).unwrap();
|
||||
let ctx = GsymContext::parse_header(&data).unwrap();
|
||||
|
||||
let idx = find_address(&ctx, 0x0000000002000000).unwrap();
|
||||
let addrinfo = ctx.addr_info(idx).unwrap();
|
||||
assert_eq!(ctx.get_str(addrinfo.name as usize).unwrap(), "main");
|
||||
|
||||
let idx = find_address(&ctx, 0x0000000002000100).unwrap();
|
||||
let addrinfo = ctx.addr_info(idx).unwrap();
|
||||
assert_eq!(ctx.get_str(addrinfo.name as usize).unwrap(), "factorial");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_address() {
|
||||
let test_gsym = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test.gsym");
|
||||
let mut gsym_fo = File::open(test_gsym).unwrap();
|
||||
let mut data = vec![];
|
||||
|
||||
const TEST_SIZE: usize = 6;
|
||||
|
||||
gsym_fo.read_to_end(&mut data).unwrap();
|
||||
|
||||
let mut addr_tab = Vec::<u8>::new();
|
||||
addr_tab.resize(TEST_SIZE * 4, 0);
|
||||
|
||||
let mut values: Vec<u32> = (0_u32..(TEST_SIZE as u32)).collect();
|
||||
|
||||
let copy_to_addr_tab = |values: &[u32], addr_tab: &mut Vec<u8>| {
|
||||
addr_tab.clear();
|
||||
for v in values {
|
||||
let r = addr_tab.write(&v.to_ne_bytes());
|
||||
assert!(r.is_ok());
|
||||
}
|
||||
};
|
||||
// Generate all possible sequences that values are in strictly
|
||||
// ascending order and `< TEST_SIZE * 2`.
|
||||
let gen_values = |values: &mut [u32]| {
|
||||
let mut carry_out = TEST_SIZE as u32 * 2;
|
||||
for i in (0..values.len()).rev() {
|
||||
values[i] += 1;
|
||||
if values[i] >= carry_out {
|
||||
carry_out -= 1;
|
||||
continue;
|
||||
}
|
||||
// Make all values at right side minimal and strictly
|
||||
// ascending.
|
||||
for j in (i + 1)..values.len() {
|
||||
values[j] = values[j - 1] + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
while values[0] <= TEST_SIZE as u32 {
|
||||
copy_to_addr_tab(&values, &mut addr_tab);
|
||||
|
||||
for addr in 0..(TEST_SIZE * 2) {
|
||||
let addr_tab = addr_tab.clone();
|
||||
let mut ctx = GsymContext::parse_header(&data).unwrap();
|
||||
ctx.header.num_addrs = TEST_SIZE as u32;
|
||||
ctx.header.addr_off_size = 4;
|
||||
ctx.header.base_address = 0;
|
||||
ctx.addr_tab = addr_tab.as_slice();
|
||||
|
||||
let idx = find_address(&ctx, addr as u64).unwrap_or(0);
|
||||
let addr_u32 = addr as u32;
|
||||
let idx1 = match values.binary_search(&addr_u32) {
|
||||
Ok(idx) => idx,
|
||||
Err(idx) => {
|
||||
// When the searching value is falling in
|
||||
// between two values, it will return the
|
||||
// index of the later one. But we want the
|
||||
// earlier one.
|
||||
if idx > 0 {
|
||||
idx - 1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
};
|
||||
assert_eq!(idx, idx1);
|
||||
}
|
||||
|
||||
gen_values(&mut values);
|
||||
}
|
||||
}
|
||||
}
|
||||
224
third_party/blazesym/src/gsym/resolver.rs
vendored
Normal file
224
third_party/blazesym/src/gsym/resolver.rs
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
use std::fs::File;
|
||||
use std::io::{Error, Read};
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::{AddressLineInfo, FindAddrOpts, SymResolver, SymbolInfo};
|
||||
|
||||
use super::linetab::run_op;
|
||||
use super::linetab::LineTableRow;
|
||||
use super::linetab::RunResult;
|
||||
use super::parser::find_address;
|
||||
use super::parser::parse_address_data;
|
||||
use super::parser::parse_line_table_header;
|
||||
use super::parser::GsymContext;
|
||||
use super::types::InfoTypeLineTableInfo;
|
||||
|
||||
/// The symbol resolver for the GSYM format.
|
||||
pub struct GsymResolver {
|
||||
file_name: PathBuf,
|
||||
ctx: GsymContext<'static>,
|
||||
_data: Vec<u8>,
|
||||
loaded_address: u64,
|
||||
}
|
||||
|
||||
impl GsymResolver {
|
||||
pub fn new(file_name: PathBuf, loaded_address: u64) -> Result<GsymResolver, Error> {
|
||||
let mut fo = File::open(&file_name)?;
|
||||
let mut data = vec![];
|
||||
fo.read_to_end(&mut data)?;
|
||||
let ctx = GsymContext::parse_header(&data)?;
|
||||
|
||||
Ok(GsymResolver {
|
||||
file_name,
|
||||
// SAFETY: the lifetime of ctx depends on data, which is
|
||||
// owned by the object. So, it is safe to strip the
|
||||
// lifetime of ctx.
|
||||
ctx: unsafe { mem::transmute(ctx) },
|
||||
_data: data,
|
||||
loaded_address,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SymResolver for GsymResolver {
|
||||
fn get_address_range(&self) -> (u64, u64) {
|
||||
let sz = self.ctx.num_addresses();
|
||||
if sz == 0 {
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
// TODO: Must not unwrap.
|
||||
let start = self.ctx.addr_at(0).unwrap() + self.loaded_address;
|
||||
// TODO: Must not unwrap.
|
||||
let end = self.ctx.addr_at(sz - 1).unwrap()
|
||||
+ self.ctx.addr_info(sz - 1).unwrap().size as u64
|
||||
+ self.loaded_address;
|
||||
(start, end)
|
||||
}
|
||||
|
||||
fn find_symbols(&self, addr: u64) -> Vec<(&str, u64)> {
|
||||
let addr = addr - self.loaded_address;
|
||||
let idx = if let Some(idx) = find_address(&self.ctx, addr) {
|
||||
idx
|
||||
} else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let found = if let Some(addr) = self.ctx.addr_at(idx) {
|
||||
addr
|
||||
} else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
if addr < found {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let info = if let Some(info) = self.ctx.addr_info(idx) {
|
||||
info
|
||||
} else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let name = if let Some(name) = self.ctx.get_str(info.name as usize) {
|
||||
name
|
||||
} else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
vec![(name, found + self.loaded_address)]
|
||||
}
|
||||
|
||||
fn find_address(&self, _name: &str, _opts: &FindAddrOpts) -> Option<Vec<SymbolInfo>> {
|
||||
// It is inefficient to find the address of a symbol with
|
||||
// GSYM. We may support it in the future if needed.
|
||||
None
|
||||
}
|
||||
|
||||
fn find_address_regex(&self, _pattern: &str, _opts: &FindAddrOpts) -> Option<Vec<SymbolInfo>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Finds the source code location for a given address.
|
||||
///
|
||||
/// This function takes in an address and returns the file path,
|
||||
/// line number and column of the line in the source code that
|
||||
/// the address corresponds to. If it doesn't find any match it
|
||||
/// returns `None`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `addr` - The address to find the source code location for.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The `AddressLineInfo` corresponding to the address or `None`.
|
||||
fn find_line_info(&self, addr: u64) -> Option<AddressLineInfo> {
|
||||
let addr = addr.checked_sub(self.loaded_address)?;
|
||||
let idx = find_address(&self.ctx, addr)?;
|
||||
let symaddr = self.ctx.addr_at(idx)?;
|
||||
if addr < symaddr {
|
||||
return None;
|
||||
}
|
||||
let addrinfo = self.ctx.addr_info(idx)?;
|
||||
if addr >= (symaddr + addrinfo.size as u64) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let addrdatas = parse_address_data(addrinfo.data);
|
||||
for adr_ent in addrdatas {
|
||||
if adr_ent.typ != InfoTypeLineTableInfo {
|
||||
continue;
|
||||
}
|
||||
// Continue to execute all GSYM line table operations
|
||||
// until the end of the buffer is reached or a row
|
||||
// containing addr is located.
|
||||
let (lntab_hdr, hdr_bytes) = parse_line_table_header(adr_ent.data)?;
|
||||
let ops = &adr_ent.data[hdr_bytes..];
|
||||
let mut lntab_row = LineTableRow::line_table_row_from(&lntab_hdr, symaddr);
|
||||
let mut last_lntab_row = lntab_row.clone();
|
||||
let mut row_cnt = 0;
|
||||
let mut pc = 0;
|
||||
while pc < ops.len() {
|
||||
match run_op(&mut lntab_row, &lntab_hdr, ops, pc) {
|
||||
RunResult::Ok(bytes) => {
|
||||
pc += bytes;
|
||||
}
|
||||
RunResult::NewRow(bytes) => {
|
||||
pc += bytes;
|
||||
row_cnt += 1;
|
||||
if addr < lntab_row.address {
|
||||
if row_cnt == 1 {
|
||||
// The address is lower than the first row.
|
||||
return None;
|
||||
}
|
||||
// Rollback to the last row.
|
||||
lntab_row = last_lntab_row;
|
||||
break;
|
||||
}
|
||||
last_lntab_row = lntab_row.clone();
|
||||
}
|
||||
RunResult::End | RunResult::Err => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if row_cnt == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let finfo = self.ctx.file_info(lntab_row.file_idx as usize)?;
|
||||
let dirname = self.ctx.get_str(finfo.directory as usize)?;
|
||||
let filename = self.ctx.get_str(finfo.filename as usize)?;
|
||||
let path = Path::new(dirname).join(filename).to_str()?.to_string();
|
||||
return Some(AddressLineInfo {
|
||||
path,
|
||||
line_no: lntab_row.file_line as usize,
|
||||
column: 0,
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn addr_file_off(&self, _addr: u64) -> Option<u64> {
|
||||
// Unavailable
|
||||
None
|
||||
}
|
||||
|
||||
fn get_obj_file_name(&self) -> &Path {
|
||||
&self.file_name
|
||||
}
|
||||
|
||||
fn repr(&self) -> String {
|
||||
format!("GSYM {:?}", self.file_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::env;
|
||||
|
||||
|
||||
/// Make sure that we can find file line information for a function, if available.
|
||||
#[test]
|
||||
fn test_find_line_info() {
|
||||
let test_gsym = Path::new(&env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.join("test.gsym");
|
||||
let resolver = GsymResolver::new(test_gsym, 0).unwrap();
|
||||
|
||||
// `main` resides at address 0x2000000, and it's located at line 19.
|
||||
let info = resolver.find_line_info(0x2000000).unwrap();
|
||||
assert_eq!(info.line_no, 19);
|
||||
assert!(info.path.ends_with("test-gsym.c"));
|
||||
|
||||
// `factorial` resides at address 0x2000100, and it's located at line 7.
|
||||
let info = resolver.find_line_info(0x2000100).unwrap();
|
||||
assert_eq!(info.line_no, 7);
|
||||
assert!(info.path.ends_with("test-gsym.c"));
|
||||
}
|
||||
}
|
||||
46
third_party/blazesym/src/gsym/types.rs
vendored
Normal file
46
third_party/blazesym/src/gsym/types.rs
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
pub const GSYM_MAGIC: u32 = 0x4753594d;
|
||||
pub const GSYM_VERSION: u16 = 1;
|
||||
|
||||
/// The size of address data offsets in GSYM.
|
||||
pub const ADDR_DATA_OFFSET_SIZE: usize = 4;
|
||||
/// The size of a GSYM `FileInfo` object.
|
||||
pub const FILE_INFO_SIZE: usize = 8;
|
||||
|
||||
/// GSYM File Header
|
||||
pub struct Header {
|
||||
pub magic: u32,
|
||||
pub version: u16,
|
||||
pub addr_off_size: u8,
|
||||
pub uuid_size: u8,
|
||||
pub base_address: u64,
|
||||
pub num_addrs: u32,
|
||||
pub strtab_offset: u32,
|
||||
pub strtab_size: u32,
|
||||
pub uuid: [u8; 20],
|
||||
}
|
||||
|
||||
pub struct FileInfo {
|
||||
pub directory: u32,
|
||||
pub filename: u32,
|
||||
}
|
||||
|
||||
pub struct AddressInfo<'a> {
|
||||
pub size: u32,
|
||||
pub name: u32,
|
||||
/// The raw data comprises a list of [`AddressData`].
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
pub struct AddressData<'a> {
|
||||
/// The data type. Its value should be one of InfoType*.
|
||||
pub typ: u32,
|
||||
pub length: u32,
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const InfoTypeEndOfList: u32 = 0;
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const InfoTypeLineTableInfo: u32 = 1;
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const InfoTypeInlineInfo: u32 = 2;
|
||||
415
third_party/blazesym/src/ksym.rs
vendored
Normal file
415
third_party/blazesym/src/ksym.rs
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
use super::{FindAddrOpts, SymbolInfo, SymbolType};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::default::Default;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Error};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use std::u64;
|
||||
|
||||
use crate::SymResolver;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
const KALLSYMS: &str = "/proc/kallsyms";
|
||||
const DFL_KSYM_CAP: usize = 200000;
|
||||
|
||||
pub struct Ksym {
|
||||
pub addr: u64,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// The symbol resolver for /proc/kallsyms.
|
||||
///
|
||||
/// The users should provide the path of kallsyms, so you can provide
|
||||
/// a copy from other devices.
|
||||
pub struct KSymResolver {
|
||||
syms: Vec<Ksym>,
|
||||
sym_to_addr: RefCell<HashMap<&'static str, u64>>,
|
||||
file_name: PathBuf,
|
||||
}
|
||||
|
||||
impl KSymResolver {
|
||||
pub fn new() -> KSymResolver {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn load_file_name(&mut self, filename: PathBuf) -> Result<(), std::io::Error> {
|
||||
let f = File::open(&filename)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
let mut line = String::new();
|
||||
|
||||
while let Ok(sz) = reader.read_line(&mut line) {
|
||||
if sz == 0 {
|
||||
break;
|
||||
}
|
||||
let tokens: Vec<&str> = line.split_whitespace().collect();
|
||||
if tokens.len() < 3 {
|
||||
break;
|
||||
}
|
||||
let (addr, _symbol, func) = (tokens[0], tokens[1], tokens[2]);
|
||||
if let Ok(addr) = u64::from_str_radix(addr, 16) {
|
||||
if addr == 0 {
|
||||
line.truncate(0);
|
||||
continue;
|
||||
}
|
||||
let name = String::from(func);
|
||||
self.syms.push(Ksym { addr, name });
|
||||
}
|
||||
|
||||
line.truncate(0);
|
||||
}
|
||||
|
||||
self.syms.sort_by(|a, b| a.addr.cmp(&b.addr));
|
||||
|
||||
self.file_name = filename;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load(&mut self) -> Result<(), std::io::Error> {
|
||||
self.load_file_name(PathBuf::from(KALLSYMS))
|
||||
}
|
||||
|
||||
fn ensure_sym_to_addr(&self) {
|
||||
if self.sym_to_addr.borrow().len() > 0 {
|
||||
return;
|
||||
}
|
||||
let mut sym_to_addr = self.sym_to_addr.borrow_mut();
|
||||
for Ksym { name, addr } in self.syms.iter() {
|
||||
// Performance & lifetime hacking
|
||||
let name_static = unsafe { &*(name as *const String) };
|
||||
sym_to_addr.insert(name_static, *addr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_addresses_ksym(&self, addr: u64) -> impl Iterator<Item = &Ksym> {
|
||||
let mut l = 0;
|
||||
let mut r = self.syms.len();
|
||||
|
||||
while l < r {
|
||||
let m = (l + r) / 2;
|
||||
let sym = &self.syms[m];
|
||||
|
||||
if addr < sym.addr {
|
||||
r = m;
|
||||
} else {
|
||||
l = m + 1;
|
||||
}
|
||||
}
|
||||
debug_assert!(
|
||||
(l == 0 || l >= self.syms.len())
|
||||
|| (self.syms[l - 1].addr <= addr && addr < self.syms[l].addr)
|
||||
);
|
||||
self.syms[0..l]
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(move |sym| sym.addr == self.syms[l - 1].addr)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_addresses_ksym_simple(&self, addr: u64) -> impl Iterator<Item = &Ksym> {
|
||||
let mut i = 0;
|
||||
while i < self.syms.len() && addr >= self.syms[i].addr {
|
||||
i += 1;
|
||||
}
|
||||
self.syms[..i]
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(move |x| x.addr == self.syms[i - 1].addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for KSymResolver {
|
||||
fn default() -> Self {
|
||||
KSymResolver {
|
||||
syms: Vec::with_capacity(DFL_KSYM_CAP),
|
||||
sym_to_addr: RefCell::new(HashMap::new()),
|
||||
file_name: PathBuf::from(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SymResolver for KSymResolver {
|
||||
fn get_address_range(&self) -> (u64, u64) {
|
||||
(0xffffffff80000000, 0xffffffffffffffff)
|
||||
}
|
||||
|
||||
fn find_symbols(&self, addr: u64) -> Vec<(&str, u64)> {
|
||||
self.find_addresses_ksym(addr)
|
||||
.map(|sym| (sym.name.as_str(), sym.addr))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn find_address(&self, name: &str, opts: &FindAddrOpts) -> Option<Vec<SymbolInfo>> {
|
||||
if let SymbolType::Variable = opts.sym_type {
|
||||
return None;
|
||||
}
|
||||
self.ensure_sym_to_addr();
|
||||
|
||||
if let Some(addr) = self.sym_to_addr.borrow().get(name) {
|
||||
return Some(vec![SymbolInfo {
|
||||
name: name.to_string(),
|
||||
address: *addr,
|
||||
size: 0,
|
||||
sym_type: SymbolType::Function,
|
||||
..Default::default()
|
||||
}]);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_address_regex(&self, pattern: &str, opts: &FindAddrOpts) -> Option<Vec<SymbolInfo>> {
|
||||
if let SymbolType::Variable = opts.sym_type {
|
||||
return None;
|
||||
}
|
||||
self.ensure_sym_to_addr();
|
||||
|
||||
let re = Regex::new(pattern).unwrap();
|
||||
let mut syms = vec![];
|
||||
for (name, addr) in self.sym_to_addr.borrow().iter() {
|
||||
if re.is_match(name) {
|
||||
syms.push(SymbolInfo {
|
||||
name: name.to_string(),
|
||||
address: *addr,
|
||||
size: 0,
|
||||
sym_type: SymbolType::Function,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(syms)
|
||||
}
|
||||
|
||||
fn find_line_info(&self, _addr: u64) -> Option<super::AddressLineInfo> {
|
||||
None
|
||||
}
|
||||
|
||||
fn addr_file_off(&self, _addr: u64) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_obj_file_name(&self) -> &Path {
|
||||
&self.file_name
|
||||
}
|
||||
|
||||
fn repr(&self) -> String {
|
||||
String::from("KSymResolver")
|
||||
}
|
||||
}
|
||||
|
||||
/// Cache of KSymResolver.
|
||||
///
|
||||
/// It returns the same isntance if path is the same.
|
||||
pub struct KSymCache {
|
||||
resolvers: RefCell<HashMap<PathBuf, Rc<KSymResolver>>>,
|
||||
}
|
||||
|
||||
impl KSymCache {
|
||||
pub fn new() -> KSymCache {
|
||||
KSymCache {
|
||||
resolvers: RefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Find an instance of KSymResolver from the cache or create a new one.
|
||||
pub fn get_resolver(&self, path: &Path) -> Result<Rc<KSymResolver>, Error> {
|
||||
let mut resolvers = self.resolvers.borrow_mut();
|
||||
if let Some(resolver) = resolvers.get(path) {
|
||||
return Ok(resolver.clone());
|
||||
}
|
||||
|
||||
let mut resolver = Rc::new(KSymResolver::new());
|
||||
Rc::get_mut(&mut resolver)
|
||||
.unwrap()
|
||||
.load_file_name(path.to_path_buf())?;
|
||||
resolvers.insert(path.to_path_buf(), resolver.clone());
|
||||
Ok(resolver)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// This test case is skipped by default for /proc/kallsyms may
|
||||
// not available in some environment.
|
||||
#[test]
|
||||
#[ignore = "system-dependent; may fail"]
|
||||
fn ksym_resolver_load_find() {
|
||||
let mut resolver = KSymResolver::new();
|
||||
assert!(resolver.load().is_ok());
|
||||
|
||||
assert!(
|
||||
resolver.syms.len() > 10000,
|
||||
"kallsyms seems to be unavailable or with all 0 addresses. (Check /proc/kallsyms)"
|
||||
);
|
||||
|
||||
// Find the address of the symbol placed at the middle
|
||||
let sym = &resolver.syms[resolver.syms.len() / 2];
|
||||
let addr = sym.addr;
|
||||
let name = sym.name.clone();
|
||||
let found = resolver.find_symbols(addr);
|
||||
assert!(!found.is_empty());
|
||||
assert!(found.iter().any(|x| x.0 == name));
|
||||
let addr = addr + 1;
|
||||
let found = resolver.find_symbols(addr);
|
||||
assert!(!found.is_empty());
|
||||
assert!(found.iter().any(|x| x.0 == name));
|
||||
|
||||
// 0 is an invalid address. We remove all symbols with 0 as
|
||||
// thier address from the list.
|
||||
let found = resolver.find_symbols(0);
|
||||
assert!(found.is_empty());
|
||||
|
||||
// Find the address of the last symbol
|
||||
let sym = &resolver.syms.last().unwrap();
|
||||
let addr = sym.addr;
|
||||
let name = sym.name.clone();
|
||||
let found = resolver.find_symbols(addr);
|
||||
assert!(!found.is_empty());
|
||||
assert!(found.iter().any(|x| x.0 == name));
|
||||
let found = resolver.find_symbols(addr + 1);
|
||||
assert!(!found.is_empty());
|
||||
assert!(found.iter().any(|x| x.0 == name));
|
||||
|
||||
// Find the symbol placed at the one third
|
||||
let sym = &resolver.syms[resolver.syms.len() / 3];
|
||||
let addr = sym.addr;
|
||||
let name = sym.name.clone();
|
||||
let opts = FindAddrOpts {
|
||||
offset_in_file: false,
|
||||
obj_file_name: false,
|
||||
sym_type: SymbolType::Function,
|
||||
};
|
||||
let found = resolver.find_address(&name, &opts);
|
||||
assert!(found.is_some());
|
||||
assert!(found.unwrap().iter().any(|x| x.address == addr));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ksym_cache() {
|
||||
let cache = KSymCache::new();
|
||||
let resolver = cache.get_resolver(Path::new(KALLSYMS));
|
||||
let resolver1 = cache.get_resolver(Path::new(KALLSYMS));
|
||||
assert!(resolver.is_ok());
|
||||
assert!(resolver1.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_addresses_ksym() {
|
||||
let mut resolver = KSymResolver::new();
|
||||
resolver.syms = vec![
|
||||
Ksym {
|
||||
addr: 0x123,
|
||||
name: "1".to_string(),
|
||||
},
|
||||
Ksym {
|
||||
addr: 0x123,
|
||||
name: "1.5".to_string(),
|
||||
},
|
||||
Ksym {
|
||||
addr: 0x1234,
|
||||
name: "2".to_string(),
|
||||
},
|
||||
Ksym {
|
||||
addr: 0x12345,
|
||||
name: "3".to_string(),
|
||||
},
|
||||
];
|
||||
|
||||
// The address is less than the smallest address of all symbols.
|
||||
assert!(resolver.find_addresses_ksym(1).next().is_none());
|
||||
|
||||
// The address match symbols exactly (the first address.)
|
||||
let syms = resolver.find_addresses_ksym(0x123).collect::<Vec<_>>();
|
||||
assert_eq!(syms.len(), 2);
|
||||
assert_eq!(syms[0].addr, 0x123);
|
||||
assert_eq!(syms[0].name, "1.5");
|
||||
assert_eq!(syms[1].addr, 0x123);
|
||||
assert_eq!(syms[1].name, "1");
|
||||
|
||||
// The address is in between two symbols (the first address.)
|
||||
let syms = resolver.find_addresses_ksym(0x124).collect::<Vec<_>>();
|
||||
assert_eq!(syms.len(), 2);
|
||||
assert_eq!(syms[0].addr, 0x123);
|
||||
assert_eq!(syms[0].name, "1.5");
|
||||
assert_eq!(syms[1].addr, 0x123);
|
||||
assert_eq!(syms[1].name, "1");
|
||||
|
||||
// The address match symbols exactly.
|
||||
let syms = resolver.find_addresses_ksym(0x1234).collect::<Vec<_>>();
|
||||
assert_eq!(syms.len(), 1);
|
||||
assert_eq!(syms[0].addr, 0x1234);
|
||||
assert_eq!(syms[0].name, "2");
|
||||
|
||||
// The address is in between two symbols.
|
||||
let syms = resolver.find_addresses_ksym(0x1235).collect::<Vec<_>>();
|
||||
assert_eq!(syms.len(), 1);
|
||||
assert_eq!(syms[0].addr, 0x1234);
|
||||
assert_eq!(syms[0].name, "2");
|
||||
|
||||
// The address match symbols exactly (the biggest address.)
|
||||
let syms = resolver.find_addresses_ksym(0x12345).collect::<Vec<_>>();
|
||||
assert_eq!(syms.len(), 1);
|
||||
assert_eq!(syms[0].addr, 0x12345);
|
||||
assert_eq!(syms[0].name, "3");
|
||||
|
||||
// The address is bigger than the biggest address of all symbols.
|
||||
let syms = resolver.find_addresses_ksym(0x1234568).collect::<Vec<_>>();
|
||||
assert_eq!(syms.len(), 1);
|
||||
assert_eq!(syms[0].addr, 0x12345);
|
||||
assert_eq!(syms[0].name, "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_addresses_ksym_exhaust() {
|
||||
let syms_sz = 10;
|
||||
let mut resolver = KSymResolver::new();
|
||||
resolver.syms = (0..syms_sz)
|
||||
.map(|x| Ksym {
|
||||
addr: 1,
|
||||
name: x.to_string(),
|
||||
})
|
||||
.collect();
|
||||
// A full-adder has a carry-out signal, right?
|
||||
// Yes! Here it is.
|
||||
let raised_carry_out = |addr| addr > syms_sz as u64;
|
||||
|
||||
while !raised_carry_out(resolver.syms[0].addr) {
|
||||
// Test find_addresses_ksym() against every address in the
|
||||
// range [0..syms_sz+1].
|
||||
for i in 0..=(syms_sz + 1) {
|
||||
let result: Vec<_> = resolver.find_addresses_ksym(i as u64).collect();
|
||||
let result_s: Vec<_> = resolver.find_addresses_ksym_simple(i as u64).collect();
|
||||
assert_eq!(result.len(), result_s.len());
|
||||
assert_eq!(
|
||||
result
|
||||
.iter()
|
||||
.map(|x| x.name.as_str())
|
||||
.cmp(result_s.iter().map(|x| x.name.as_str())),
|
||||
Ordering::Equal
|
||||
);
|
||||
}
|
||||
|
||||
let mut i = syms_sz - 1;
|
||||
// Increase the address of the last symbol.
|
||||
resolver.syms[i].addr += 1;
|
||||
while i > 0 && raised_carry_out(resolver.syms[i].addr) {
|
||||
// Bring the raised carry-out it to the left.
|
||||
i -= 1;
|
||||
resolver.syms[i].addr += 1;
|
||||
}
|
||||
// Every symbol on the right side have a raised carry-out.
|
||||
// Reset their addresses.
|
||||
let low_addr = resolver.syms[i].addr;
|
||||
while i < (syms_sz - 1) {
|
||||
i += 1;
|
||||
resolver.syms[i].addr = low_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1058
third_party/blazesym/src/lib.rs
vendored
Normal file
1058
third_party/blazesym/src/lib.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
444
third_party/blazesym/src/util.rs
vendored
Normal file
444
third_party/blazesym/src/util.rs
vendored
Normal file
@@ -0,0 +1,444 @@
|
||||
use std::ffi::CStr;
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader, Error, ErrorKind};
|
||||
use std::mem::size_of;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
pub fn search_address_key<T, V: Ord>(
|
||||
data: &[T],
|
||||
address: V,
|
||||
keyfn: &dyn Fn(&T) -> V,
|
||||
) -> Option<usize> {
|
||||
let mut left = 0;
|
||||
let mut right = data.len();
|
||||
|
||||
if right == 0 {
|
||||
return None;
|
||||
}
|
||||
if address < keyfn(&data[0]) {
|
||||
return None;
|
||||
}
|
||||
|
||||
while (left + 1) < right {
|
||||
let v = (left + right) / 2;
|
||||
let key = keyfn(&data[v]);
|
||||
|
||||
if key == address {
|
||||
return Some(v);
|
||||
}
|
||||
if address < key {
|
||||
right = v;
|
||||
} else {
|
||||
left = v;
|
||||
}
|
||||
}
|
||||
|
||||
Some(left)
|
||||
}
|
||||
|
||||
/// Do binary search but skip entries not having a key.
|
||||
pub fn search_address_opt_key<T, V: Ord>(
|
||||
data: &[T],
|
||||
address: V,
|
||||
keyfn: &dyn Fn(&T) -> Option<V>,
|
||||
) -> Option<usize> {
|
||||
let mut left = 0;
|
||||
let mut right = data.len();
|
||||
|
||||
while left < right {
|
||||
let left_key = keyfn(&data[left]);
|
||||
if left_key.is_some() {
|
||||
break;
|
||||
}
|
||||
left += 1;
|
||||
}
|
||||
|
||||
if left == right {
|
||||
return None;
|
||||
}
|
||||
|
||||
if address < keyfn(&data[left]).unwrap() {
|
||||
return None;
|
||||
}
|
||||
|
||||
while (left + 1) < right {
|
||||
let mut v = (left + right) / 2;
|
||||
|
||||
let v_saved = v;
|
||||
// Skip entries not having a key
|
||||
while v < right {
|
||||
let key = keyfn(&data[v]);
|
||||
if key.is_some() {
|
||||
break;
|
||||
}
|
||||
v += 1;
|
||||
}
|
||||
// All entries at the right side haven't keys.
|
||||
// Shrink to the left side.
|
||||
if v == right {
|
||||
right = v_saved;
|
||||
continue;
|
||||
}
|
||||
|
||||
let key = keyfn(&data[v]).unwrap();
|
||||
|
||||
if key == address {
|
||||
return Some(v);
|
||||
}
|
||||
if address < key {
|
||||
right = v;
|
||||
} else {
|
||||
left = v;
|
||||
}
|
||||
}
|
||||
|
||||
Some(left)
|
||||
}
|
||||
|
||||
pub fn extract_string(raw: &[u8], off: usize) -> Option<&str> {
|
||||
let mut end = off;
|
||||
|
||||
if off >= raw.len() {
|
||||
return None;
|
||||
}
|
||||
while end < raw.len() && raw[end] != 0 {
|
||||
end += 1;
|
||||
}
|
||||
if end >= raw.len() {
|
||||
return None;
|
||||
}
|
||||
CStr::from_bytes_with_nul(&raw[off..=end])
|
||||
.ok()?
|
||||
.to_str()
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub struct LinuxMapsEntry {
|
||||
pub loaded_address: u64,
|
||||
pub end_address: u64,
|
||||
pub mode: u8,
|
||||
pub offset: u64,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
pub fn parse_maps(pid: u32) -> Result<Vec<LinuxMapsEntry>, Error> {
|
||||
let mut entries = Vec::<LinuxMapsEntry>::new();
|
||||
let file_name = if pid == 0 {
|
||||
String::from("/proc/self/maps")
|
||||
} else {
|
||||
format!("/proc/{pid}/maps")
|
||||
};
|
||||
let file = fs::File::open(file_name)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let mut line = String::new();
|
||||
let re_ptn = Regex::new(
|
||||
r"^([0-9a-f]+)-([0-9a-f]+) ([rwxp\\-]+) ([0-9a-f]+) [0-9a-f]+:[0-9a-f]+ [0-9]+ *((/[^/]+)+)$",
|
||||
);
|
||||
if re_ptn.is_err() {
|
||||
println!("{re_ptn:?}");
|
||||
return Err(Error::new(ErrorKind::InvalidData, "Failed to build regex"));
|
||||
}
|
||||
let re_ptn = re_ptn.unwrap();
|
||||
|
||||
while reader.read_line(&mut line)? > 0 {
|
||||
if let Some(caps) = re_ptn.captures(&line) {
|
||||
let loaded_address_str = caps.get(1).unwrap().as_str();
|
||||
let loaded_address = u64::from_str_radix(loaded_address_str, 16).unwrap();
|
||||
|
||||
let end_address_str = caps.get(2).unwrap().as_str();
|
||||
let end_address = u64::from_str_radix(end_address_str, 16).unwrap();
|
||||
|
||||
let mode_str = caps.get(3).unwrap().as_str();
|
||||
let mut mode = 0;
|
||||
for c in mode_str.chars() {
|
||||
mode = (mode << 1) | u8::from(c != '-');
|
||||
}
|
||||
|
||||
let offset = u64::from_str_radix(caps.get(4).unwrap().as_str(), 16).unwrap();
|
||||
let path = caps.get(5).unwrap().as_str().strip_suffix('\n').unwrap();
|
||||
let mut path_str = path.to_string();
|
||||
if let Some(pos) = path.rfind(" (deleted)") {
|
||||
if pos == path.len() - " (deleted)".len() {
|
||||
path_str = format!("/proc/{pid}/map_files/{loaded_address:x}-{end_address:x}");
|
||||
}
|
||||
}
|
||||
|
||||
let entry = LinuxMapsEntry {
|
||||
loaded_address,
|
||||
end_address,
|
||||
mode,
|
||||
offset,
|
||||
path: PathBuf::from(path_str),
|
||||
};
|
||||
entries.push(entry);
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_leb128_128(mut data: &[u8]) -> Option<(u128, u8)> {
|
||||
data.read_u128_leb128()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_leb128(mut data: &[u8]) -> Option<(u64, u8)> {
|
||||
data.read_u128_leb128().map(|(v, s)| (v as u64, s))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_leb128_s(mut data: &[u8]) -> Option<(i64, u8)> {
|
||||
data.read_i128_leb128().map(|(v, s)| (v as i64, s))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_uhalf(mut data: &[u8]) -> u16 {
|
||||
// TODO: Need to handle errors more gracefully.
|
||||
data.read_u16().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_uword(mut data: &[u8]) -> u32 {
|
||||
// TODO: Need to handle errors more gracefully.
|
||||
data.read_u32().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_udword(mut data: &[u8]) -> u64 {
|
||||
// TODO: Need to handle errors more gracefully.
|
||||
data.read_u64().unwrap()
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
/// A marker trait for "plain old data" data types.
|
||||
///
|
||||
/// # Safety
|
||||
/// Only safe to implement for types that are valid for any bit pattern.
|
||||
pub unsafe trait Pod {}
|
||||
|
||||
unsafe impl Pod for i8 {}
|
||||
unsafe impl Pod for u8 {}
|
||||
unsafe impl Pod for i16 {}
|
||||
unsafe impl Pod for u16 {}
|
||||
unsafe impl Pod for i32 {}
|
||||
unsafe impl Pod for u32 {}
|
||||
unsafe impl Pod for i64 {}
|
||||
unsafe impl Pod for u64 {}
|
||||
unsafe impl Pod for i128 {}
|
||||
unsafe impl Pod for u128 {}
|
||||
}
|
||||
|
||||
/// An trait providing utility functions for reading data from a byte buffer.
|
||||
pub trait ReadRaw<'data> {
|
||||
/// Ensure that `len` bytes are available for consumption.
|
||||
fn ensure(&self, len: usize) -> Option<()>;
|
||||
|
||||
/// Consume and return `len` bytes.
|
||||
fn read_slice(&mut self, len: usize) -> Option<&'data [u8]>;
|
||||
|
||||
/// Read a NUL terminated string.
|
||||
fn read_cstr(&mut self) -> Option<&'data CStr>;
|
||||
|
||||
/// Read anything implementing `Pod`.
|
||||
#[inline]
|
||||
fn read_pod<T>(&mut self) -> Option<T>
|
||||
where
|
||||
T: sealed::Pod,
|
||||
{
|
||||
let data = self.read_slice(size_of::<T>())?;
|
||||
// SAFETY: `T` is `Pod` and hence valid for any bit pattern. The pointer
|
||||
// is guaranteed to be valid and to point to memory of at least
|
||||
// `sizeof(T)` bytes.
|
||||
let value = unsafe { data.as_ptr().cast::<T>().read_unaligned() };
|
||||
Some(value)
|
||||
}
|
||||
|
||||
/// Read a `u8` value.
|
||||
#[inline]
|
||||
fn read_u8(&mut self) -> Option<u8> {
|
||||
self.read_pod::<u8>()
|
||||
}
|
||||
|
||||
/// Read a `i16` value.
|
||||
#[inline]
|
||||
fn read_i16(&mut self) -> Option<i16> {
|
||||
self.read_pod::<i16>()
|
||||
}
|
||||
|
||||
/// Read a `u16` value.
|
||||
#[inline]
|
||||
fn read_u16(&mut self) -> Option<u16> {
|
||||
self.read_pod::<u16>()
|
||||
}
|
||||
|
||||
/// Read a `i32` value.
|
||||
#[inline]
|
||||
fn read_i32(&mut self) -> Option<i32> {
|
||||
self.read_pod::<i32>()
|
||||
}
|
||||
|
||||
/// Read a `u32` value.
|
||||
#[inline]
|
||||
fn read_u32(&mut self) -> Option<u32> {
|
||||
self.read_pod::<u32>()
|
||||
}
|
||||
|
||||
/// Read a `u64` value.
|
||||
#[inline]
|
||||
fn read_u64(&mut self) -> Option<u64> {
|
||||
self.read_pod::<u64>()
|
||||
}
|
||||
|
||||
/// Read a `u128` encoded as unsigned variable length little endian base 128
|
||||
/// value.
|
||||
///
|
||||
/// The function returns the value read along with the number of bytes
|
||||
/// consumed.
|
||||
fn read_u128_leb128(&mut self) -> Option<(u128, u8)> {
|
||||
let mut shift = 0;
|
||||
let mut value = 0u128;
|
||||
while let Some(bytes) = self.read_slice(1) {
|
||||
if let [byte] = bytes {
|
||||
value |= ((byte & 0b0111_1111) as u128) << shift;
|
||||
shift += 7;
|
||||
if (byte & 0b1000_0000) == 0 {
|
||||
return Some((value, shift / 7));
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Read a `u128` encoded as signed variable length little endian base 128
|
||||
/// value.
|
||||
///
|
||||
/// The function returns the value read along with the number of bytes
|
||||
/// consumed.
|
||||
fn read_i128_leb128(&mut self) -> Option<(i128, u8)> {
|
||||
let (value, shift) = self.read_u128_leb128()?;
|
||||
let sign_bits = 128 - shift * 7;
|
||||
let value = ((value as i128) << sign_bits) >> sign_bits;
|
||||
Some((value, shift))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'data> ReadRaw<'data> for &'data [u8] {
|
||||
#[inline]
|
||||
fn ensure(&self, len: usize) -> Option<()> {
|
||||
if len > self.len() {
|
||||
return None;
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_slice(&mut self, len: usize) -> Option<&'data [u8]> {
|
||||
self.ensure(len)?;
|
||||
let (a, b) = self.split_at(len);
|
||||
*self = b;
|
||||
Some(a)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_cstr(&mut self) -> Option<&'data CStr> {
|
||||
let idx = self.iter().position(|byte| *byte == b'\0')?;
|
||||
CStr::from_bytes_with_nul(self.read_slice(idx + 1)?).ok()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
/// Make sure that `[u8]::ensure` works as expected.
|
||||
#[test]
|
||||
fn u8_slice_len_ensurance() {
|
||||
let slice = [0u8; 0].as_slice();
|
||||
assert_eq!(slice.ensure(0), Some(()));
|
||||
assert_eq!(slice.ensure(1), None);
|
||||
|
||||
let slice = [1u8].as_slice();
|
||||
assert_eq!(slice.ensure(0), Some(()));
|
||||
assert_eq!(slice.ensure(1), Some(()));
|
||||
assert_eq!(slice.ensure(2), None);
|
||||
}
|
||||
|
||||
/// Check that we can read various integers from a slice.
|
||||
#[test]
|
||||
fn pod_reading() {
|
||||
macro_rules! test {
|
||||
($type:ty) => {{
|
||||
let max = <$type>::MAX.to_ne_bytes();
|
||||
let one = (1 as $type).to_ne_bytes();
|
||||
|
||||
let mut data = Vec::new();
|
||||
let () = data.extend_from_slice(&max);
|
||||
let () = data.extend_from_slice(&one);
|
||||
let () = data.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
|
||||
let mut raw = data.as_slice();
|
||||
let uword = raw.read_pod::<$type>().unwrap();
|
||||
assert_eq!(uword, <$type>::MAX);
|
||||
|
||||
let uword = raw.read_pod::<$type>().unwrap();
|
||||
assert_eq!(uword, 1);
|
||||
}};
|
||||
}
|
||||
|
||||
test!(i8);
|
||||
test!(u8);
|
||||
test!(i16);
|
||||
test!(u16);
|
||||
test!(i32);
|
||||
test!(u32);
|
||||
test!(i64);
|
||||
test!(u64);
|
||||
test!(i128);
|
||||
test!(u128);
|
||||
}
|
||||
|
||||
/// Test reading of signed and unsigned 16 and 32 bit values against known
|
||||
/// results.
|
||||
#[test]
|
||||
fn word_reading() {
|
||||
let data = 0xf936857fu32.to_ne_bytes();
|
||||
assert_eq!(data.as_slice().read_u16().unwrap(), 0x857f);
|
||||
assert_eq!(data.as_slice().read_i16().unwrap(), -31361);
|
||||
assert_eq!(data.as_slice().read_u32().unwrap(), 0xf936857f);
|
||||
assert_eq!(data.as_slice().read_i32().unwrap(), -113867393);
|
||||
}
|
||||
|
||||
/// Make sure that we can read leb128 encoded values.
|
||||
#[test]
|
||||
fn leb128_reading() {
|
||||
let data = [0xf4, 0xf3, 0x75];
|
||||
let (v, s) = data.as_slice().read_u128_leb128().unwrap();
|
||||
assert_eq!(v, 0x1d79f4);
|
||||
assert_eq!(s, 3);
|
||||
|
||||
let (v, s) = data.as_slice().read_i128_leb128().unwrap();
|
||||
assert_eq!(v, -165388);
|
||||
assert_eq!(s, 3);
|
||||
}
|
||||
|
||||
/// Check that we can read a NUL terminated string from a slice.
|
||||
#[test]
|
||||
fn cstr_reading() {
|
||||
let mut slice = b"abc\x001234".as_slice();
|
||||
|
||||
let cstr = slice.read_cstr().unwrap();
|
||||
assert_eq!(cstr, CStr::from_bytes_with_nul(b"abc\0").unwrap());
|
||||
|
||||
// No terminating NUL byte.
|
||||
let mut slice = b"abc".as_slice();
|
||||
assert_eq!(slice.read_cstr(), None);
|
||||
}
|
||||
}
|
||||
1
third_party/bpftool/.gitattributes
vendored
Normal file
1
third_party/bpftool/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
src/Makefile.* linguist-language=Makefile
|
||||
3
third_party/bpftool/.gitmodules
vendored
Normal file
3
third_party/bpftool/.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "libbpf"]
|
||||
path = libbpf
|
||||
url = https://github.com/libbpf/libbpf.git
|
||||
1
third_party/bpftool/BPF-CHECKPOINT-COMMIT
vendored
Normal file
1
third_party/bpftool/BPF-CHECKPOINT-COMMIT
vendored
Normal file
@@ -0,0 +1 @@
|
||||
496720b7cfb6574a8f6f4d434f23e3d1e6cfaeb9
|
||||
1
third_party/bpftool/CHECKPOINT-COMMIT
vendored
Normal file
1
third_party/bpftool/CHECKPOINT-COMMIT
vendored
Normal file
@@ -0,0 +1 @@
|
||||
a3e7e6b17946f48badce98d7ac360678a0ea7393
|
||||
39
third_party/bpftool/Dockerfile
vendored
Normal file
39
third_party/bpftool/Dockerfile
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# With this Dockerfile, you can create a container image:
|
||||
# $ docker build -f Dockerfile -t bpftool .
|
||||
# And then use it:
|
||||
# $ docker run --rm -ti --privileged --pid=host bpftool prog
|
||||
|
||||
# hadolint global ignore=DL3008
|
||||
|
||||
FROM ubuntu:22.04 as builder
|
||||
|
||||
RUN \
|
||||
export DEBIAN_FRONTEND=noninteractive && \
|
||||
apt-get update && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
build-essential \
|
||||
libelf-dev \
|
||||
libz-dev \
|
||||
libcap-dev \
|
||||
clang llvm llvm-dev lld \
|
||||
binutils-dev \
|
||||
pkg-config && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY . /src
|
||||
RUN \
|
||||
make -C /src/src clean && \
|
||||
make -C /src/src -j "$(nproc)"
|
||||
|
||||
FROM ubuntu:22.04
|
||||
RUN \
|
||||
export DEBIAN_FRONTEND=noninteractive && \
|
||||
apt-get update && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
libelf1 \
|
||||
llvm && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /src/src/bpftool /bin/bpftool
|
||||
|
||||
ENTRYPOINT ["/bin/bpftool"]
|
||||
1
third_party/bpftool/LICENSE
vendored
Normal file
1
third_party/bpftool/LICENSE
vendored
Normal file
@@ -0,0 +1 @@
|
||||
GPL-2.0-only OR BSD-2-Clause
|
||||
32
third_party/bpftool/LICENSE.BSD-2-Clause
vendored
Normal file
32
third_party/bpftool/LICENSE.BSD-2-Clause
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
Valid-License-Identifier: BSD-2-Clause
|
||||
SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
|
||||
Usage-Guide:
|
||||
To use the BSD 2-clause "Simplified" License put the following SPDX
|
||||
tag/value pair into a comment according to the placement guidelines in
|
||||
the licensing rules documentation:
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
License-Text:
|
||||
|
||||
Copyright (c) <year> <owner> . All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
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.
|
||||
339
third_party/bpftool/LICENSE.GPL-2.0
vendored
Normal file
339
third_party/bpftool/LICENSE.GPL-2.0
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
1211
third_party/bpftool/bash-completion/bpftool
vendored
Normal file
1211
third_party/bpftool/bash-completion/bpftool
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
third_party/bpftool/docs/.gitignore
vendored
Normal file
2
third_party/bpftool/docs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
bpftool*.8
|
||||
60
third_party/bpftool/docs/Makefile
vendored
Normal file
60
third_party/bpftool/docs/Makefile
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
include ../src/Makefile.include
|
||||
|
||||
INSTALL ?= install
|
||||
RM ?= rm -f
|
||||
RMDIR ?= rmdir --ignore-fail-on-non-empty
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
prefix ?= /usr/local
|
||||
mandir ?= $(prefix)/man
|
||||
man8dir = $(mandir)/man8
|
||||
|
||||
MAN8_RST = $(wildcard bpftool*.rst)
|
||||
|
||||
_DOC_MAN8 = $(patsubst %.rst,%.8,$(MAN8_RST))
|
||||
DOC_MAN8 = $(addprefix $(OUTPUT),$(_DOC_MAN8))
|
||||
|
||||
man: man8
|
||||
man8: $(DOC_MAN8)
|
||||
|
||||
RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null)
|
||||
RST2MAN_OPTS += --verbose --strip-comments
|
||||
|
||||
list_pages = $(sort $(basename $(filter-out $(1),$(MAN8_RST))))
|
||||
see_also = $(subst " ",, \
|
||||
"\n" \
|
||||
"SEE ALSO\n" \
|
||||
"========\n" \
|
||||
"\t**bpf**\ (2),\n" \
|
||||
"\t**bpf-helpers**\\ (7)" \
|
||||
$(foreach page,$(call list_pages,$(1)),",\n\t**$(page)**\\ (8)") \
|
||||
"\n")
|
||||
|
||||
$(OUTPUT)%.8: %.rst
|
||||
ifndef RST2MAN_DEP
|
||||
$(error "rst2man not found, but required to generate man pages")
|
||||
endif
|
||||
$(QUIET_GEN)( cat $< ; printf "%b" $(call see_also,$<) ) | rst2man $(RST2MAN_OPTS) > $@
|
||||
|
||||
clean:
|
||||
$(call QUIET_CLEAN, Documentation)
|
||||
$(Q)$(RM) $(DOC_MAN8)
|
||||
|
||||
install: man
|
||||
$(call QUIET_INSTALL, Documentation-man)
|
||||
$(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir)
|
||||
$(Q)$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir)
|
||||
|
||||
uninstall:
|
||||
$(call QUIET_UNINST, Documentation-man)
|
||||
$(Q)$(RM) $(addprefix $(DESTDIR)$(man8dir)/,$(_DOC_MAN8))
|
||||
$(Q)$(RMDIR) $(DESTDIR)$(man8dir)
|
||||
|
||||
.PHONY: man man8 clean install uninstall
|
||||
.DEFAULT_GOAL := man
|
||||
268
third_party/bpftool/docs/bpftool-btf.rst
vendored
Normal file
268
third_party/bpftool/docs/bpftool-btf.rst
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-btf
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection of BTF data
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **btf** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| | { **-B** | **--base-btf** } }
|
||||
|
||||
*COMMANDS* := { **dump** | **help** }
|
||||
|
||||
BTF COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
|
||||
| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
|
||||
| **bpftool** **btf help**
|
||||
|
|
||||
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
|
||||
| *FORMAT* := { **raw** | **c** }
|
||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool btf { show | list }** [**id** *BTF_ID*]
|
||||
Show information about loaded BTF objects. If a BTF ID is
|
||||
specified, show information only about given BTF object,
|
||||
otherwise list all BTF objects currently loaded on the
|
||||
system.
|
||||
|
||||
Since Linux 5.8 bpftool is able to discover information about
|
||||
processes that hold open file descriptors (FDs) against BTF
|
||||
objects. On such kernels bpftool will automatically emit this
|
||||
information as well.
|
||||
|
||||
**bpftool btf dump** *BTF_SRC*
|
||||
Dump BTF entries from a given *BTF_SRC*.
|
||||
|
||||
When **id** is specified, BTF object with that ID will be
|
||||
loaded and all its BTF types emitted.
|
||||
|
||||
When **map** is provided, it's expected that map has
|
||||
associated BTF object with BTF types describing key and
|
||||
value. It's possible to select whether to dump only BTF
|
||||
type(s) associated with key (**key**), value (**value**),
|
||||
both key and value (**kv**), or all BTF types present in
|
||||
associated BTF object (**all**). If not specified, **kv**
|
||||
is assumed.
|
||||
|
||||
When **prog** is provided, it's expected that program has
|
||||
associated BTF object with BTF types.
|
||||
|
||||
When specifying *FILE*, an ELF file is expected, containing
|
||||
.BTF section with well-defined BTF binary format data,
|
||||
typically produced by clang or pahole.
|
||||
|
||||
**format** option can be used to override default (raw)
|
||||
output format. Raw (**raw**) or C-syntax (**c**) output
|
||||
formats are supported.
|
||||
|
||||
**bpftool btf help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
-B, --base-btf *FILE*
|
||||
Pass a base BTF object. Base BTF objects are typically used
|
||||
with BTF objects for kernel modules. To avoid duplicating
|
||||
all kernel symbols required by modules, BTF objects for
|
||||
modules are "split", they are built incrementally on top of
|
||||
the kernel (vmlinux) BTF object. So the base BTF reference
|
||||
should usually point to the kernel BTF.
|
||||
|
||||
When the main BTF object to process (for example, the
|
||||
module BTF to dump) is passed as a *FILE*, bpftool attempts
|
||||
to autodetect the path for the base object, and passing
|
||||
this option is optional. When the main BTF object is passed
|
||||
through other handles, this option becomes necessary.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool btf dump id 1226**
|
||||
|
||||
::
|
||||
|
||||
[1] PTR '(anon)' type_id=2
|
||||
[2] STRUCT 'dummy_tracepoint_args' size=16 vlen=2
|
||||
'pad' type_id=3 bits_offset=0
|
||||
'sock' type_id=4 bits_offset=64
|
||||
[3] INT 'long long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
|
||||
[4] PTR '(anon)' type_id=5
|
||||
[5] FWD 'sock' fwd_kind=union
|
||||
|
||||
This gives an example of default output for all supported BTF kinds.
|
||||
|
||||
**$ cat prog.c**
|
||||
|
||||
::
|
||||
|
||||
struct fwd_struct;
|
||||
|
||||
enum my_enum {
|
||||
VAL1 = 3,
|
||||
VAL2 = 7,
|
||||
};
|
||||
|
||||
typedef struct my_struct my_struct_t;
|
||||
|
||||
struct my_struct {
|
||||
const unsigned int const_int_field;
|
||||
int bitfield_field: 4;
|
||||
char arr_field[16];
|
||||
const struct fwd_struct *restrict fwd_field;
|
||||
enum my_enum enum_field;
|
||||
volatile my_struct_t *typedef_ptr_field;
|
||||
};
|
||||
|
||||
union my_union {
|
||||
int a;
|
||||
struct my_struct b;
|
||||
};
|
||||
|
||||
struct my_struct struct_global_var __attribute__((section("data_sec"))) = {
|
||||
.bitfield_field = 3,
|
||||
.enum_field = VAL1,
|
||||
};
|
||||
int global_var __attribute__((section("data_sec"))) = 7;
|
||||
|
||||
__attribute__((noinline))
|
||||
int my_func(union my_union *arg1, int arg2)
|
||||
{
|
||||
static int static_var __attribute__((section("data_sec"))) = 123;
|
||||
static_var++;
|
||||
return static_var;
|
||||
}
|
||||
|
||||
**$ bpftool btf dump file prog.o**
|
||||
|
||||
::
|
||||
|
||||
[1] PTR '(anon)' type_id=2
|
||||
[2] UNION 'my_union' size=48 vlen=2
|
||||
'a' type_id=3 bits_offset=0
|
||||
'b' type_id=4 bits_offset=0
|
||||
[3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
|
||||
[4] STRUCT 'my_struct' size=48 vlen=6
|
||||
'const_int_field' type_id=5 bits_offset=0
|
||||
'bitfield_field' type_id=3 bits_offset=32 bitfield_size=4
|
||||
'arr_field' type_id=8 bits_offset=40
|
||||
'fwd_field' type_id=10 bits_offset=192
|
||||
'enum_field' type_id=14 bits_offset=256
|
||||
'typedef_ptr_field' type_id=15 bits_offset=320
|
||||
[5] CONST '(anon)' type_id=6
|
||||
[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
|
||||
[7] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
|
||||
[8] ARRAY '(anon)' type_id=7 index_type_id=9 nr_elems=16
|
||||
[9] INT '__ARRAY_SIZE_TYPE__' size=4 bits_offset=0 nr_bits=32 encoding=(none)
|
||||
[10] RESTRICT '(anon)' type_id=11
|
||||
[11] PTR '(anon)' type_id=12
|
||||
[12] CONST '(anon)' type_id=13
|
||||
[13] FWD 'fwd_struct' fwd_kind=union
|
||||
[14] ENUM 'my_enum' size=4 vlen=2
|
||||
'VAL1' val=3
|
||||
'VAL2' val=7
|
||||
[15] PTR '(anon)' type_id=16
|
||||
[16] VOLATILE '(anon)' type_id=17
|
||||
[17] TYPEDEF 'my_struct_t' type_id=4
|
||||
[18] FUNC_PROTO '(anon)' ret_type_id=3 vlen=2
|
||||
'arg1' type_id=1
|
||||
'arg2' type_id=3
|
||||
[19] FUNC 'my_func' type_id=18
|
||||
[20] VAR 'struct_global_var' type_id=4, linkage=global-alloc
|
||||
[21] VAR 'global_var' type_id=3, linkage=global-alloc
|
||||
[22] VAR 'my_func.static_var' type_id=3, linkage=static
|
||||
[23] DATASEC 'data_sec' size=0 vlen=3
|
||||
type_id=20 offset=0 size=48
|
||||
type_id=21 offset=0 size=4
|
||||
type_id=22 offset=52 size=4
|
||||
|
||||
The following commands print BTF types associated with specified map's key,
|
||||
value, both key and value, and all BTF types, respectively. By default, both
|
||||
key and value types will be printed.
|
||||
|
||||
**# bpftool btf dump map id 123 key**
|
||||
|
||||
::
|
||||
|
||||
[39] TYPEDEF 'u32' type_id=37
|
||||
|
||||
**# bpftool btf dump map id 123 value**
|
||||
|
||||
::
|
||||
|
||||
[86] PTR '(anon)' type_id=87
|
||||
|
||||
**# bpftool btf dump map id 123 kv**
|
||||
|
||||
::
|
||||
|
||||
[39] TYPEDEF 'u32' type_id=37
|
||||
[86] PTR '(anon)' type_id=87
|
||||
|
||||
**# bpftool btf dump map id 123 all**
|
||||
|
||||
::
|
||||
|
||||
[1] PTR '(anon)' type_id=0
|
||||
.
|
||||
.
|
||||
.
|
||||
[2866] ARRAY '(anon)' type_id=52 index_type_id=51 nr_elems=4
|
||||
|
||||
All the standard ways to specify map or program are supported:
|
||||
|
||||
**# bpftool btf dump map id 123**
|
||||
|
||||
**# bpftool btf dump map pinned /sys/fs/bpf/map_name**
|
||||
|
||||
**# bpftool btf dump prog id 456**
|
||||
|
||||
**# bpftool btf dump prog tag b88e0a09b1d9759d**
|
||||
|
||||
**# bpftool btf dump prog pinned /sys/fs/bpf/prog_name**
|
||||
|
||||
|
|
||||
| **# bpftool btf dump file /sys/kernel/btf/i2c_smbus**
|
||||
| (or)
|
||||
| **# I2C_SMBUS_ID=$(bpftool btf show -p | jq '.[] | select(.name=="i2c_smbus").id')**
|
||||
| **# bpftool btf dump id ${I2C_SMBUS_ID} -B /sys/kernel/btf/vmlinux**
|
||||
|
||||
::
|
||||
|
||||
[104848] STRUCT 'i2c_smbus_alert' size=40 vlen=2
|
||||
'alert' type_id=393 bits_offset=0
|
||||
'ara' type_id=56050 bits_offset=256
|
||||
[104849] STRUCT 'alert_data' size=12 vlen=3
|
||||
'addr' type_id=16 bits_offset=0
|
||||
'type' type_id=56053 bits_offset=32
|
||||
'data' type_id=7 bits_offset=64
|
||||
[104850] PTR '(anon)' type_id=104848
|
||||
[104851] PTR '(anon)' type_id=104849
|
||||
[104852] FUNC 'i2c_register_spd' type_id=84745 linkage=static
|
||||
[104853] FUNC 'smbalert_driver_init' type_id=1213 linkage=static
|
||||
[104854] FUNC_PROTO '(anon)' ret_type_id=18 vlen=1
|
||||
'ara' type_id=56050
|
||||
[104855] FUNC 'i2c_handle_smbus_alert' type_id=104854 linkage=static
|
||||
[104856] FUNC 'smbalert_remove' type_id=104854 linkage=static
|
||||
[104857] FUNC_PROTO '(anon)' ret_type_id=18 vlen=2
|
||||
'ara' type_id=56050
|
||||
'id' type_id=56056
|
||||
[104858] FUNC 'smbalert_probe' type_id=104857 linkage=static
|
||||
[104859] FUNC 'smbalert_work' type_id=9695 linkage=static
|
||||
[104860] FUNC 'smbus_alert' type_id=71367 linkage=static
|
||||
[104861] FUNC 'smbus_do_alert' type_id=84827 linkage=static
|
||||
157
third_party/bpftool/docs/bpftool-cgroup.rst
vendored
Normal file
157
third_party/bpftool/docs/bpftool-cgroup.rst
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-cgroup
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF progs
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **cgroup** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| | { **-f** | **--bpffs** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **list** | **tree** | **attach** | **detach** | **help** }
|
||||
|
||||
CGROUP COMMANDS
|
||||
===============
|
||||
|
||||
| **bpftool** **cgroup** { **show** | **list** } *CGROUP* [**effective**]
|
||||
| **bpftool** **cgroup tree** [*CGROUP_ROOT*] [**effective**]
|
||||
| **bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
|
||||
| **bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
|
||||
| **bpftool** **cgroup help**
|
||||
|
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
|
||||
| *ATTACH_TYPE* := { **cgroup_inet_ingress** | **cgroup_inet_egress** |
|
||||
| **cgroup_inet_sock_create** | **cgroup_sock_ops** |
|
||||
| **cgroup_device** | **cgroup_inet4_bind** | **cgroup_inet6_bind** |
|
||||
| **cgroup_inet4_post_bind** | **cgroup_inet6_post_bind** |
|
||||
| **cgroup_inet4_connect** | **cgroup_inet6_connect** |
|
||||
| **cgroup_inet4_getpeername** | **cgroup_inet6_getpeername** |
|
||||
| **cgroup_inet4_getsockname** | **cgroup_inet6_getsockname** |
|
||||
| **cgroup_udp4_sendmsg** | **cgroup_udp6_sendmsg** |
|
||||
| **cgroup_udp4_recvmsg** | **cgroup_udp6_recvmsg** |
|
||||
| **cgroup_sysctl** | **cgroup_getsockopt** | **cgroup_setsockopt** |
|
||||
| **cgroup_inet_sock_release** }
|
||||
| *ATTACH_FLAGS* := { **multi** | **override** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool cgroup { show | list }** *CGROUP* [**effective**]
|
||||
List all programs attached to the cgroup *CGROUP*.
|
||||
|
||||
Output will start with program ID followed by attach type,
|
||||
attach flags and program name.
|
||||
|
||||
If **effective** is specified retrieve effective programs that
|
||||
will execute for events within a cgroup. This includes
|
||||
inherited along with attached ones.
|
||||
|
||||
**bpftool cgroup tree** [*CGROUP_ROOT*] [**effective**]
|
||||
Iterate over all cgroups in *CGROUP_ROOT* and list all
|
||||
attached programs. If *CGROUP_ROOT* is not specified,
|
||||
bpftool uses cgroup v2 mountpoint.
|
||||
|
||||
The output is similar to the output of cgroup show/list
|
||||
commands: it starts with absolute cgroup path, followed by
|
||||
program ID, attach type, attach flags and program name.
|
||||
|
||||
If **effective** is specified retrieve effective programs that
|
||||
will execute for events within a cgroup. This includes
|
||||
inherited along with attached ones.
|
||||
|
||||
**bpftool cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
|
||||
Attach program *PROG* to the cgroup *CGROUP* with attach type
|
||||
*ATTACH_TYPE* and optional *ATTACH_FLAGS*.
|
||||
|
||||
*ATTACH_FLAGS* can be one of: **override** if a sub-cgroup installs
|
||||
some bpf program, the program in this cgroup yields to sub-cgroup
|
||||
program; **multi** if a sub-cgroup installs some bpf program,
|
||||
that cgroup program gets run in addition to the program in this
|
||||
cgroup.
|
||||
|
||||
Only one program is allowed to be attached to a cgroup with
|
||||
no attach flags or the **override** flag. Attaching another
|
||||
program will release old program and attach the new one.
|
||||
|
||||
Multiple programs are allowed to be attached to a cgroup with
|
||||
**multi**. They are executed in FIFO order (those that were
|
||||
attached first, run first).
|
||||
|
||||
Non-default *ATTACH_FLAGS* are supported by kernel version 4.14
|
||||
and later.
|
||||
|
||||
*ATTACH_TYPE* can be on of:
|
||||
**ingress** ingress path of the inet socket (since 4.10);
|
||||
**egress** egress path of the inet socket (since 4.10);
|
||||
**sock_create** opening of an inet socket (since 4.10);
|
||||
**sock_ops** various socket operations (since 4.12);
|
||||
**device** device access (since 4.15);
|
||||
**bind4** call to bind(2) for an inet4 socket (since 4.17);
|
||||
**bind6** call to bind(2) for an inet6 socket (since 4.17);
|
||||
**post_bind4** return from bind(2) for an inet4 socket (since 4.17);
|
||||
**post_bind6** return from bind(2) for an inet6 socket (since 4.17);
|
||||
**connect4** call to connect(2) for an inet4 socket (since 4.17);
|
||||
**connect6** call to connect(2) for an inet6 socket (since 4.17);
|
||||
**sendmsg4** call to sendto(2), sendmsg(2), sendmmsg(2) for an
|
||||
unconnected udp4 socket (since 4.18);
|
||||
**sendmsg6** call to sendto(2), sendmsg(2), sendmmsg(2) for an
|
||||
unconnected udp6 socket (since 4.18);
|
||||
**recvmsg4** call to recvfrom(2), recvmsg(2), recvmmsg(2) for
|
||||
an unconnected udp4 socket (since 5.2);
|
||||
**recvmsg6** call to recvfrom(2), recvmsg(2), recvmmsg(2) for
|
||||
an unconnected udp6 socket (since 5.2);
|
||||
**sysctl** sysctl access (since 5.2);
|
||||
**getsockopt** call to getsockopt (since 5.3);
|
||||
**setsockopt** call to setsockopt (since 5.3);
|
||||
**getpeername4** call to getpeername(2) for an inet4 socket (since 5.8);
|
||||
**getpeername6** call to getpeername(2) for an inet6 socket (since 5.8);
|
||||
**getsockname4** call to getsockname(2) for an inet4 socket (since 5.8);
|
||||
**getsockname6** call to getsockname(2) for an inet6 socket (since 5.8).
|
||||
**sock_release** closing an userspace inet socket (since 5.9).
|
||||
|
||||
**bpftool cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
|
||||
Detach *PROG* from the cgroup *CGROUP* and attach type
|
||||
*ATTACH_TYPE*.
|
||||
|
||||
**bpftool prog help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
-f, --bpffs
|
||||
Show file names of pinned programs.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
|
||||
| **# mount -t bpf none /sys/fs/bpf/**
|
||||
| **# mkdir /sys/fs/cgroup/test.slice**
|
||||
| **# bpftool prog load ./device_cgroup.o /sys/fs/bpf/prog**
|
||||
| **# bpftool cgroup attach /sys/fs/cgroup/test.slice/ device id 1 allow_multi**
|
||||
|
||||
**# bpftool cgroup list /sys/fs/cgroup/test.slice/**
|
||||
|
||||
::
|
||||
|
||||
ID AttachType AttachFlags Name
|
||||
1 device allow_multi bpf_prog1
|
||||
|
||||
|
|
||||
| **# bpftool cgroup detach /sys/fs/cgroup/test.slice/ device id 1**
|
||||
| **# bpftool cgroup list /sys/fs/cgroup/test.slice/**
|
||||
|
||||
::
|
||||
|
||||
ID AttachType AttachFlags Name
|
||||
90
third_party/bpftool/docs/bpftool-feature.rst
vendored
Normal file
90
third_party/bpftool/docs/bpftool-feature.rst
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
===============
|
||||
bpftool-feature
|
||||
===============
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection of eBPF-related parameters for Linux kernel or net device
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **feature** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| }
|
||||
|
||||
*COMMANDS* := { **probe** | **help** }
|
||||
|
||||
FEATURE COMMANDS
|
||||
================
|
||||
|
||||
| **bpftool** **feature probe** [*COMPONENT*] [**full**] [**unprivileged**] [**macros** [**prefix** *PREFIX*]]
|
||||
| **bpftool** **feature list_builtins** *GROUP*
|
||||
| **bpftool** **feature help**
|
||||
|
|
||||
| *COMPONENT* := { **kernel** | **dev** *NAME* }
|
||||
| *GROUP* := { **prog_types** | **map_types** | **attach_types** | **link_types** | **helpers** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool feature probe** [**kernel**] [**full**] [**macros** [**prefix** *PREFIX*]]
|
||||
Probe the running kernel and dump a number of eBPF-related
|
||||
parameters, such as availability of the **bpf**\ () system call,
|
||||
JIT status, eBPF program types availability, eBPF helper
|
||||
functions availability, and more.
|
||||
|
||||
By default, bpftool **does not run probes** for
|
||||
**bpf_probe_write_user**\ () and **bpf_trace_printk**\()
|
||||
helpers which print warnings to kernel logs. To enable them
|
||||
and run all probes, the **full** keyword should be used.
|
||||
|
||||
If the **macros** keyword (but not the **-j** option) is
|
||||
passed, a subset of the output is dumped as a list of
|
||||
**#define** macros that are ready to be included in a C
|
||||
header file, for example. If, additionally, **prefix** is
|
||||
used to define a *PREFIX*, the provided string will be used
|
||||
as a prefix to the names of the macros: this can be used to
|
||||
avoid conflicts on macro names when including the output of
|
||||
this command as a header file.
|
||||
|
||||
Keyword **kernel** can be omitted. If no probe target is
|
||||
specified, probing the kernel is the default behaviour.
|
||||
|
||||
When the **unprivileged** keyword is used, bpftool will dump
|
||||
only the features available to a user who does not have the
|
||||
**CAP_SYS_ADMIN** capability set. The features available in
|
||||
that case usually represent a small subset of the parameters
|
||||
supported by the system. Unprivileged users MUST use the
|
||||
**unprivileged** keyword: This is to avoid misdetection if
|
||||
bpftool is inadvertently run as non-root, for example. This
|
||||
keyword is unavailable if bpftool was compiled without
|
||||
libcap.
|
||||
|
||||
**bpftool feature probe dev** *NAME* [**full**] [**macros** [**prefix** *PREFIX*]]
|
||||
Probe network device for supported eBPF features and dump
|
||||
results to the console.
|
||||
|
||||
The keywords **full**, **macros** and **prefix** have the
|
||||
same role as when probing the kernel.
|
||||
|
||||
**bpftool feature list_builtins** *GROUP*
|
||||
List items known to bpftool. These can be BPF program types
|
||||
(**prog_types**), BPF map types (**map_types**), attach types
|
||||
(**attach_types**), link types (**link_types**), or BPF helper
|
||||
functions (**helpers**). The command does not probe the system, but
|
||||
simply lists the elements that bpftool knows from compilation time,
|
||||
as provided from libbpf (for all object types) or from the BPF UAPI
|
||||
header (list of helpers). This can be used in scripts to iterate over
|
||||
BPF types or helpers.
|
||||
|
||||
**bpftool feature help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
446
third_party/bpftool/docs/bpftool-gen.rst
vendored
Normal file
446
third_party/bpftool/docs/bpftool-gen.rst
vendored
Normal file
@@ -0,0 +1,446 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-gen
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for BPF code-generation
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **gen** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } }
|
||||
|
||||
*COMMAND* := { **object** | **skeleton** | **help** }
|
||||
|
||||
GEN COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
|
||||
| **bpftool** **gen skeleton** *FILE* [**name** *OBJECT_NAME*]
|
||||
| **bpftool** **gen subskeleton** *FILE* [**name** *OBJECT_NAME*]
|
||||
| **bpftool** **gen min_core_btf** *INPUT* *OUTPUT* *OBJECT* [*OBJECT*...]
|
||||
| **bpftool** **gen help**
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
|
||||
Statically link (combine) together one or more *INPUT_FILE*'s
|
||||
into a single resulting *OUTPUT_FILE*. All the files involved
|
||||
are BPF ELF object files.
|
||||
|
||||
The rules of BPF static linking are mostly the same as for
|
||||
user-space object files, but in addition to combining data
|
||||
and instruction sections, .BTF and .BTF.ext (if present in
|
||||
any of the input files) data are combined together. .BTF
|
||||
data is deduplicated, so all the common types across
|
||||
*INPUT_FILE*'s will only be represented once in the resulting
|
||||
BTF information.
|
||||
|
||||
BPF static linking allows to partition BPF source code into
|
||||
individually compiled files that are then linked into
|
||||
a single resulting BPF object file, which can be used to
|
||||
generated BPF skeleton (with **gen skeleton** command) or
|
||||
passed directly into **libbpf** (using **bpf_object__open()**
|
||||
family of APIs).
|
||||
|
||||
**bpftool gen skeleton** *FILE*
|
||||
Generate BPF skeleton C header file for a given *FILE*.
|
||||
|
||||
BPF skeleton is an alternative interface to existing libbpf
|
||||
APIs for working with BPF objects. Skeleton code is intended
|
||||
to significantly shorten and simplify code to load and work
|
||||
with BPF programs from userspace side. Generated code is
|
||||
tailored to specific input BPF object *FILE*, reflecting its
|
||||
structure by listing out available maps, program, variables,
|
||||
etc. Skeleton eliminates the need to lookup mentioned
|
||||
components by name. Instead, if skeleton instantiation
|
||||
succeeds, they are populated in skeleton structure as valid
|
||||
libbpf types (e.g., **struct bpf_map** pointer) and can be
|
||||
passed to existing generic libbpf APIs.
|
||||
|
||||
In addition to simple and reliable access to maps and
|
||||
programs, skeleton provides a storage for BPF links (**struct
|
||||
bpf_link**) for each BPF program within BPF object. When
|
||||
requested, supported BPF programs will be automatically
|
||||
attached and resulting BPF links stored for further use by
|
||||
user in pre-allocated fields in skeleton struct. For BPF
|
||||
programs that can't be automatically attached by libbpf,
|
||||
user can attach them manually, but store resulting BPF link
|
||||
in per-program link field. All such set up links will be
|
||||
automatically destroyed on BPF skeleton destruction. This
|
||||
eliminates the need for users to manage links manually and
|
||||
rely on libbpf support to detach programs and free up
|
||||
resources.
|
||||
|
||||
Another facility provided by BPF skeleton is an interface to
|
||||
global variables of all supported kinds: mutable, read-only,
|
||||
as well as extern ones. This interface allows to pre-setup
|
||||
initial values of variables before BPF object is loaded and
|
||||
verified by kernel. For non-read-only variables, the same
|
||||
interface can be used to fetch values of global variables on
|
||||
userspace side, even if they are modified by BPF code.
|
||||
|
||||
During skeleton generation, contents of source BPF object
|
||||
*FILE* is embedded within generated code and is thus not
|
||||
necessary to keep around. This ensures skeleton and BPF
|
||||
object file are matching 1-to-1 and always stay in sync.
|
||||
Generated code is dual-licensed under LGPL-2.1 and
|
||||
BSD-2-Clause licenses.
|
||||
|
||||
It is a design goal and guarantee that skeleton interfaces
|
||||
are interoperable with generic libbpf APIs. User should
|
||||
always be able to use skeleton API to create and load BPF
|
||||
object, and later use libbpf APIs to keep working with
|
||||
specific maps, programs, etc.
|
||||
|
||||
As part of skeleton, few custom functions are generated.
|
||||
Each of them is prefixed with object name. Object name can
|
||||
either be derived from object file name, i.e., if BPF object
|
||||
file name is **example.o**, BPF object name will be
|
||||
**example**. Object name can be also specified explicitly
|
||||
through **name** *OBJECT_NAME* parameter. The following
|
||||
custom functions are provided (assuming **example** as
|
||||
the object name):
|
||||
|
||||
- **example__open** and **example__open_opts**.
|
||||
These functions are used to instantiate skeleton. It
|
||||
corresponds to libbpf's **bpf_object__open**\ () API.
|
||||
**_opts** variants accepts extra **bpf_object_open_opts**
|
||||
options.
|
||||
|
||||
- **example__load**.
|
||||
This function creates maps, loads and verifies BPF
|
||||
programs, initializes global data maps. It corresponds to
|
||||
libppf's **bpf_object__load**\ () API.
|
||||
|
||||
- **example__open_and_load** combines **example__open** and
|
||||
**example__load** invocations in one commonly used
|
||||
operation.
|
||||
|
||||
- **example__attach** and **example__detach**
|
||||
This pair of functions allow to attach and detach,
|
||||
correspondingly, already loaded BPF object. Only BPF
|
||||
programs of types supported by libbpf for auto-attachment
|
||||
will be auto-attached and their corresponding BPF links
|
||||
instantiated. For other BPF programs, user can manually
|
||||
create a BPF link and assign it to corresponding fields in
|
||||
skeleton struct. **example__detach** will detach both
|
||||
links created automatically, as well as those populated by
|
||||
user manually.
|
||||
|
||||
- **example__destroy**
|
||||
Detach and unload BPF programs, free up all the resources
|
||||
used by skeleton and BPF object.
|
||||
|
||||
If BPF object has global variables, corresponding structs
|
||||
with memory layout corresponding to global data data section
|
||||
layout will be created. Currently supported ones are: *.data*,
|
||||
*.bss*, *.rodata*, and *.kconfig* structs/data sections.
|
||||
These data sections/structs can be used to set up initial
|
||||
values of variables, if set before **example__load**.
|
||||
Afterwards, if target kernel supports memory-mapped BPF
|
||||
arrays, same structs can be used to fetch and update
|
||||
(non-read-only) data from userspace, with same simplicity
|
||||
as for BPF side.
|
||||
|
||||
**bpftool gen subskeleton** *FILE*
|
||||
Generate BPF subskeleton C header file for a given *FILE*.
|
||||
|
||||
Subskeletons are similar to skeletons, except they do not own
|
||||
the corresponding maps, programs, or global variables. They
|
||||
require that the object file used to generate them is already
|
||||
loaded into a *bpf_object* by some other means.
|
||||
|
||||
This functionality is useful when a library is included into a
|
||||
larger BPF program. A subskeleton for the library would have
|
||||
access to all objects and globals defined in it, without
|
||||
having to know about the larger program.
|
||||
|
||||
Consequently, there are only two functions defined
|
||||
for subskeletons:
|
||||
|
||||
- **example__open(bpf_object\*)**
|
||||
Instantiates a subskeleton from an already opened (but not
|
||||
necessarily loaded) **bpf_object**.
|
||||
|
||||
- **example__destroy()**
|
||||
Frees the storage for the subskeleton but *does not* unload
|
||||
any BPF programs or maps.
|
||||
|
||||
**bpftool** **gen min_core_btf** *INPUT* *OUTPUT* *OBJECT* [*OBJECT*...]
|
||||
Generate a minimum BTF file as *OUTPUT*, derived from a given
|
||||
*INPUT* BTF file, containing all needed BTF types so one, or
|
||||
more, given eBPF objects CO-RE relocations may be satisfied.
|
||||
|
||||
When kernels aren't compiled with CONFIG_DEBUG_INFO_BTF,
|
||||
libbpf, when loading an eBPF object, has to rely on external
|
||||
BTF files to be able to calculate CO-RE relocations.
|
||||
|
||||
Usually, an external BTF file is built from existing kernel
|
||||
DWARF data using pahole. It contains all the types used by
|
||||
its respective kernel image and, because of that, is big.
|
||||
|
||||
The min_core_btf feature builds smaller BTF files, customized
|
||||
to one or multiple eBPF objects, so they can be distributed
|
||||
together with an eBPF CO-RE based application, turning the
|
||||
application portable to different kernel versions.
|
||||
|
||||
Check examples bellow for more information how to use it.
|
||||
|
||||
**bpftool gen help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
-L, --use-loader
|
||||
For skeletons, generate a "light" skeleton (also known as "loader"
|
||||
skeleton). A light skeleton contains a loader eBPF program. It does
|
||||
not use the majority of the libbpf infrastructure, and does not need
|
||||
libelf.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**$ cat example1.bpf.c**
|
||||
|
||||
::
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
const volatile int param1 = 42;
|
||||
bool global_flag = true;
|
||||
struct { int x; } data = {};
|
||||
|
||||
SEC("raw_tp/sys_enter")
|
||||
int handle_sys_enter(struct pt_regs *ctx)
|
||||
{
|
||||
static long my_static_var;
|
||||
if (global_flag)
|
||||
my_static_var++;
|
||||
else
|
||||
data.x += param1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
**$ cat example2.bpf.c**
|
||||
|
||||
::
|
||||
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 128);
|
||||
__type(key, int);
|
||||
__type(value, long);
|
||||
} my_map SEC(".maps");
|
||||
|
||||
SEC("raw_tp/sys_exit")
|
||||
int handle_sys_exit(struct pt_regs *ctx)
|
||||
{
|
||||
int zero = 0;
|
||||
bpf_map_lookup_elem(&my_map, &zero);
|
||||
return 0;
|
||||
}
|
||||
|
||||
This is example BPF application with two BPF programs and a mix of BPF maps
|
||||
and global variables. Source code is split across two source code files.
|
||||
|
||||
**$ clang --target=bpf -g example1.bpf.c -o example1.bpf.o**
|
||||
|
||||
**$ clang --target=bpf -g example2.bpf.c -o example2.bpf.o**
|
||||
|
||||
**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o**
|
||||
|
||||
This set of commands compiles *example1.bpf.c* and *example2.bpf.c*
|
||||
individually and then statically links respective object files into the final
|
||||
BPF ELF object file *example.bpf.o*.
|
||||
|
||||
**$ bpftool gen skeleton example.bpf.o name example | tee example.skel.h**
|
||||
|
||||
::
|
||||
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/* THIS FILE IS AUTOGENERATED! */
|
||||
#ifndef __EXAMPLE_SKEL_H__
|
||||
#define __EXAMPLE_SKEL_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
struct example {
|
||||
struct bpf_object_skeleton *skeleton;
|
||||
struct bpf_object *obj;
|
||||
struct {
|
||||
struct bpf_map *rodata;
|
||||
struct bpf_map *data;
|
||||
struct bpf_map *bss;
|
||||
struct bpf_map *my_map;
|
||||
} maps;
|
||||
struct {
|
||||
struct bpf_program *handle_sys_enter;
|
||||
struct bpf_program *handle_sys_exit;
|
||||
} progs;
|
||||
struct {
|
||||
struct bpf_link *handle_sys_enter;
|
||||
struct bpf_link *handle_sys_exit;
|
||||
} links;
|
||||
struct example__bss {
|
||||
struct {
|
||||
int x;
|
||||
} data;
|
||||
} *bss;
|
||||
struct example__data {
|
||||
_Bool global_flag;
|
||||
long int handle_sys_enter_my_static_var;
|
||||
} *data;
|
||||
struct example__rodata {
|
||||
int param1;
|
||||
} *rodata;
|
||||
};
|
||||
|
||||
static void example__destroy(struct example *obj);
|
||||
static inline struct example *example__open_opts(
|
||||
const struct bpf_object_open_opts *opts);
|
||||
static inline struct example *example__open();
|
||||
static inline int example__load(struct example *obj);
|
||||
static inline struct example *example__open_and_load();
|
||||
static inline int example__attach(struct example *obj);
|
||||
static inline void example__detach(struct example *obj);
|
||||
|
||||
#endif /* __EXAMPLE_SKEL_H__ */
|
||||
|
||||
**$ cat example.c**
|
||||
|
||||
::
|
||||
|
||||
#include "example.skel.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
struct example *skel;
|
||||
int err = 0;
|
||||
|
||||
skel = example__open();
|
||||
if (!skel)
|
||||
goto cleanup;
|
||||
|
||||
skel->rodata->param1 = 128;
|
||||
|
||||
err = example__load(skel);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
err = example__attach(skel);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
/* all libbpf APIs are usable */
|
||||
printf("my_map name: %s\n", bpf_map__name(skel->maps.my_map));
|
||||
printf("sys_enter prog FD: %d\n",
|
||||
bpf_program__fd(skel->progs.handle_sys_enter));
|
||||
|
||||
/* detach and re-attach sys_exit program */
|
||||
bpf_link__destroy(skel->links.handle_sys_exit);
|
||||
skel->links.handle_sys_exit =
|
||||
bpf_program__attach(skel->progs.handle_sys_exit);
|
||||
|
||||
printf("my_static_var: %ld\n",
|
||||
skel->bss->handle_sys_enter_my_static_var);
|
||||
|
||||
cleanup:
|
||||
example__destroy(skel);
|
||||
return err;
|
||||
}
|
||||
|
||||
**# ./example**
|
||||
|
||||
::
|
||||
|
||||
my_map name: my_map
|
||||
sys_enter prog FD: 8
|
||||
my_static_var: 7
|
||||
|
||||
This is a stripped-out version of skeleton generated for above example code.
|
||||
|
||||
min_core_btf
|
||||
------------
|
||||
|
||||
**$ bpftool btf dump file 5.4.0-example.btf format raw**
|
||||
|
||||
::
|
||||
|
||||
[1] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
|
||||
[2] CONST '(anon)' type_id=1
|
||||
[3] VOLATILE '(anon)' type_id=1
|
||||
[4] ARRAY '(anon)' type_id=1 index_type_id=21 nr_elems=2
|
||||
[5] PTR '(anon)' type_id=8
|
||||
[6] CONST '(anon)' type_id=5
|
||||
[7] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none)
|
||||
[8] CONST '(anon)' type_id=7
|
||||
[9] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
|
||||
<long output>
|
||||
|
||||
**$ bpftool btf dump file one.bpf.o format raw**
|
||||
|
||||
::
|
||||
|
||||
[1] PTR '(anon)' type_id=2
|
||||
[2] STRUCT 'trace_event_raw_sys_enter' size=64 vlen=4
|
||||
'ent' type_id=3 bits_offset=0
|
||||
'id' type_id=7 bits_offset=64
|
||||
'args' type_id=9 bits_offset=128
|
||||
'__data' type_id=12 bits_offset=512
|
||||
[3] STRUCT 'trace_entry' size=8 vlen=4
|
||||
'type' type_id=4 bits_offset=0
|
||||
'flags' type_id=5 bits_offset=16
|
||||
'preempt_count' type_id=5 bits_offset=24
|
||||
<long output>
|
||||
|
||||
**$ bpftool gen min_core_btf 5.4.0-example.btf 5.4.0-smaller.btf one.bpf.o**
|
||||
|
||||
**$ bpftool btf dump file 5.4.0-smaller.btf format raw**
|
||||
|
||||
::
|
||||
|
||||
[1] TYPEDEF 'pid_t' type_id=6
|
||||
[2] STRUCT 'trace_event_raw_sys_enter' size=64 vlen=1
|
||||
'args' type_id=4 bits_offset=128
|
||||
[3] STRUCT 'task_struct' size=9216 vlen=2
|
||||
'pid' type_id=1 bits_offset=17920
|
||||
'real_parent' type_id=7 bits_offset=18048
|
||||
[4] ARRAY '(anon)' type_id=5 index_type_id=8 nr_elems=6
|
||||
[5] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
|
||||
[6] TYPEDEF '__kernel_pid_t' type_id=8
|
||||
[7] PTR '(anon)' type_id=3
|
||||
[8] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
|
||||
<end>
|
||||
|
||||
Now, the "5.4.0-smaller.btf" file may be used by libbpf as an external BTF file
|
||||
when loading the "one.bpf.o" object into the "5.4.0-example" kernel. Note that
|
||||
the generated BTF file won't allow other eBPF objects to be loaded, just the
|
||||
ones given to min_core_btf.
|
||||
|
||||
::
|
||||
|
||||
LIBBPF_OPTS(bpf_object_open_opts, opts, .btf_custom_path = "5.4.0-smaller.btf");
|
||||
struct bpf_object *obj;
|
||||
|
||||
obj = bpf_object__open_file("one.bpf.o", &opts);
|
||||
|
||||
...
|
||||
76
third_party/bpftool/docs/bpftool-iter.rst
vendored
Normal file
76
third_party/bpftool/docs/bpftool-iter.rst
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
============
|
||||
bpftool-iter
|
||||
============
|
||||
-------------------------------------------------------------------------------
|
||||
tool to create BPF iterators
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **iter** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| }
|
||||
|
||||
*COMMANDS* := { **pin** | **help** }
|
||||
|
||||
ITER COMMANDS
|
||||
===================
|
||||
|
||||
| **bpftool** **iter pin** *OBJ* *PATH* [**map** *MAP*]
|
||||
| **bpftool** **iter help**
|
||||
|
|
||||
| *OBJ* := /a/file/of/bpf_iter_target.o
|
||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool iter pin** *OBJ* *PATH* [**map** *MAP*]
|
||||
A bpf iterator combines a kernel iterating of
|
||||
particular kernel data (e.g., tasks, bpf_maps, etc.)
|
||||
and a bpf program called for each kernel data object
|
||||
(e.g., one task, one bpf_map, etc.). User space can
|
||||
*read* kernel iterator output through *read()* syscall.
|
||||
|
||||
The *pin* command creates a bpf iterator from *OBJ*,
|
||||
and pin it to *PATH*. The *PATH* should be located
|
||||
in *bpffs* mount. It must not contain a dot
|
||||
character ('.'), which is reserved for future extensions
|
||||
of *bpffs*.
|
||||
|
||||
Map element bpf iterator requires an additional parameter
|
||||
*MAP* so bpf program can iterate over map elements for
|
||||
that map. User can have a bpf program in kernel to run
|
||||
with each map element, do checking, filtering, aggregation,
|
||||
etc. without copying data to user space.
|
||||
|
||||
User can then *cat PATH* to see the bpf iterator output.
|
||||
|
||||
**bpftool iter help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool iter pin bpf_iter_netlink.o /sys/fs/bpf/my_netlink**
|
||||
|
||||
::
|
||||
|
||||
Create a file-based bpf iterator from bpf_iter_netlink.o and pin it
|
||||
to /sys/fs/bpf/my_netlink
|
||||
|
||||
**# bpftool iter pin bpf_iter_hashmap.o /sys/fs/bpf/my_hashmap map id 20**
|
||||
|
||||
::
|
||||
|
||||
Create a file-based bpf iterator from bpf_iter_hashmap.o and map with
|
||||
id 20, and pin it to /sys/fs/bpf/my_hashmap
|
||||
112
third_party/bpftool/docs/bpftool-link.rst
vendored
Normal file
112
third_party/bpftool/docs/bpftool-link.rst
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-link
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF links
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **link** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| | { **-f** | **--bpffs** } | { **-n** | **--nomount** } }
|
||||
|
||||
*COMMANDS* := { **show** | **list** | **pin** | **help** }
|
||||
|
||||
LINK COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **link { show | list }** [*LINK*]
|
||||
| **bpftool** **link pin** *LINK* *FILE*
|
||||
| **bpftool** **link detach** *LINK*
|
||||
| **bpftool** **link help**
|
||||
|
|
||||
| *LINK* := { **id** *LINK_ID* | **pinned** *FILE* }
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool link { show | list }** [*LINK*]
|
||||
Show information about active links. If *LINK* is
|
||||
specified show information only about given link,
|
||||
otherwise list all links currently active on the system.
|
||||
|
||||
Output will start with link ID followed by link type and
|
||||
zero or more named attributes, some of which depend on type
|
||||
of link.
|
||||
|
||||
Since Linux 5.8 bpftool is able to discover information about
|
||||
processes that hold open file descriptors (FDs) against BPF
|
||||
links. On such kernels bpftool will automatically emit this
|
||||
information as well.
|
||||
|
||||
**bpftool link pin** *LINK* *FILE*
|
||||
Pin link *LINK* as *FILE*.
|
||||
|
||||
Note: *FILE* must be located in *bpffs* mount. It must not
|
||||
contain a dot character ('.'), which is reserved for future
|
||||
extensions of *bpffs*.
|
||||
|
||||
**bpftool link detach** *LINK*
|
||||
Force-detach link *LINK*. BPF link and its underlying BPF
|
||||
program will stay valid, but they will be detached from the
|
||||
respective BPF hook and BPF link will transition into
|
||||
a defunct state until last open file descriptor for that
|
||||
link is closed.
|
||||
|
||||
**bpftool link help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
-f, --bpffs
|
||||
When showing BPF links, show file names of pinned
|
||||
links.
|
||||
|
||||
-n, --nomount
|
||||
Do not automatically attempt to mount any virtual file system
|
||||
(such as tracefs or BPF virtual file system) when necessary.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool link show**
|
||||
|
||||
::
|
||||
|
||||
10: cgroup prog 25
|
||||
cgroup_id 614 attach_type egress
|
||||
pids test_progs(223)
|
||||
|
||||
**# bpftool --json --pretty link show**
|
||||
|
||||
::
|
||||
|
||||
[{
|
||||
"type": "cgroup",
|
||||
"prog_id": 25,
|
||||
"cgroup_id": 614,
|
||||
"attach_type": "egress",
|
||||
"pids": [{
|
||||
"pid": 223,
|
||||
"comm": "test_progs"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
||||
| **# bpftool link pin id 10 /sys/fs/bpf/link**
|
||||
| **# ls -l /sys/fs/bpf/**
|
||||
|
||||
::
|
||||
|
||||
-rw------- 1 root root 0 Apr 23 21:39 link
|
||||
277
third_party/bpftool/docs/bpftool-map.rst
vendored
Normal file
277
third_party/bpftool/docs/bpftool-map.rst
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-map
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF maps
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **map** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| | { **-f** | **--bpffs** } | { **-n** | **--nomount** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext** |
|
||||
**delete** | **pin** | **help** }
|
||||
|
||||
MAP COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **map** { **show** | **list** } [*MAP*]
|
||||
| **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \
|
||||
| **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] \
|
||||
| [**offload_dev** *NAME*]
|
||||
| **bpftool** **map dump** *MAP*
|
||||
| **bpftool** **map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
|
||||
| **bpftool** **map lookup** *MAP* [**key** *DATA*]
|
||||
| **bpftool** **map getnext** *MAP* [**key** *DATA*]
|
||||
| **bpftool** **map delete** *MAP* **key** *DATA*
|
||||
| **bpftool** **map pin** *MAP* *FILE*
|
||||
| **bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*]
|
||||
| **bpftool** **map peek** *MAP*
|
||||
| **bpftool** **map push** *MAP* **value** *VALUE*
|
||||
| **bpftool** **map pop** *MAP*
|
||||
| **bpftool** **map enqueue** *MAP* **value** *VALUE*
|
||||
| **bpftool** **map dequeue** *MAP*
|
||||
| **bpftool** **map freeze** *MAP*
|
||||
| **bpftool** **map help**
|
||||
|
|
||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* | **name** *MAP_NAME* }
|
||||
| *DATA* := { [**hex**] *BYTES* }
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
|
||||
| *VALUE* := { *DATA* | *MAP* | *PROG* }
|
||||
| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** }
|
||||
| *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash**
|
||||
| | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash**
|
||||
| | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
|
||||
| | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
|
||||
| | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage**
|
||||
| | **queue** | **stack** | **sk_storage** | **struct_ops** | **ringbuf** | **inode_storage**
|
||||
| | **task_storage** | **bloom_filter** | **user_ringbuf** | **cgrp_storage** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool map { show | list }** [*MAP*]
|
||||
Show information about loaded maps. If *MAP* is specified
|
||||
show information only about given maps, otherwise list all
|
||||
maps currently loaded on the system. In case of **name**,
|
||||
*MAP* may match several maps which will all be shown.
|
||||
|
||||
Output will start with map ID followed by map type and
|
||||
zero or more named attributes (depending on kernel version).
|
||||
|
||||
Since Linux 5.8 bpftool is able to discover information about
|
||||
processes that hold open file descriptors (FDs) against BPF
|
||||
maps. On such kernels bpftool will automatically emit this
|
||||
information as well.
|
||||
|
||||
**bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] [**offload_dev** *NAME*]
|
||||
Create a new map with given parameters and pin it to *bpffs*
|
||||
as *FILE*.
|
||||
|
||||
*FLAGS* should be an integer which is the combination of
|
||||
desired flags, e.g. 1024 for **BPF_F_MMAPABLE** (see bpf.h
|
||||
UAPI header for existing flags).
|
||||
|
||||
To create maps of type array-of-maps or hash-of-maps, the
|
||||
**inner_map** keyword must be used to pass an inner map. The
|
||||
kernel needs it to collect metadata related to the inner maps
|
||||
that the new map will work with.
|
||||
|
||||
Keyword **offload_dev** expects a network interface name,
|
||||
and is used to request hardware offload for the map.
|
||||
|
||||
**bpftool map dump** *MAP*
|
||||
Dump all entries in a given *MAP*. In case of **name**,
|
||||
*MAP* may match several maps which will all be dumped.
|
||||
|
||||
**bpftool map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
|
||||
Update map entry for a given *KEY*.
|
||||
|
||||
*UPDATE_FLAGS* can be one of: **any** update existing entry
|
||||
or add if doesn't exit; **exist** update only if entry already
|
||||
exists; **noexist** update only if entry doesn't exist.
|
||||
|
||||
If the **hex** keyword is provided in front of the bytes
|
||||
sequence, the bytes are parsed as hexadecimal values, even if
|
||||
no "0x" prefix is added. If the keyword is not provided, then
|
||||
the bytes are parsed as decimal values, unless a "0x" prefix
|
||||
(for hexadecimal) or a "0" prefix (for octal) is provided.
|
||||
|
||||
**bpftool map lookup** *MAP* [**key** *DATA*]
|
||||
Lookup **key** in the map.
|
||||
|
||||
**bpftool map getnext** *MAP* [**key** *DATA*]
|
||||
Get next key. If *key* is not specified, get first key.
|
||||
|
||||
**bpftool map delete** *MAP* **key** *DATA*
|
||||
Remove entry from the map.
|
||||
|
||||
**bpftool map pin** *MAP* *FILE*
|
||||
Pin map *MAP* as *FILE*.
|
||||
|
||||
Note: *FILE* must be located in *bpffs* mount. It must not
|
||||
contain a dot character ('.'), which is reserved for future
|
||||
extensions of *bpffs*.
|
||||
|
||||
**bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*]
|
||||
Read events from a **BPF_MAP_TYPE_PERF_EVENT_ARRAY** map.
|
||||
|
||||
Install perf rings into a perf event array map and dump
|
||||
output of any **bpf_perf_event_output**\ () call in the kernel.
|
||||
By default read the number of CPUs on the system and
|
||||
install perf ring for each CPU in the corresponding index
|
||||
in the array.
|
||||
|
||||
If **cpu** and **index** are specified, install perf ring
|
||||
for given **cpu** at **index** in the array (single ring).
|
||||
|
||||
Note that installing a perf ring into an array will silently
|
||||
replace any existing ring. Any other application will stop
|
||||
receiving events if it installed its rings earlier.
|
||||
|
||||
**bpftool map peek** *MAP*
|
||||
Peek next value in the queue or stack.
|
||||
|
||||
**bpftool map push** *MAP* **value** *VALUE*
|
||||
Push *VALUE* onto the stack.
|
||||
|
||||
**bpftool map pop** *MAP*
|
||||
Pop and print value from the stack.
|
||||
|
||||
**bpftool map enqueue** *MAP* **value** *VALUE*
|
||||
Enqueue *VALUE* into the queue.
|
||||
|
||||
**bpftool map dequeue** *MAP*
|
||||
Dequeue and print value from the queue.
|
||||
|
||||
**bpftool map freeze** *MAP*
|
||||
Freeze the map as read-only from user space. Entries from a
|
||||
frozen map can not longer be updated or deleted with the
|
||||
**bpf**\ () system call. This operation is not reversible,
|
||||
and the map remains immutable from user space until its
|
||||
destruction. However, read and write permissions for BPF
|
||||
programs to the map remain unchanged.
|
||||
|
||||
**bpftool map help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
-f, --bpffs
|
||||
Show file names of pinned maps.
|
||||
|
||||
-n, --nomount
|
||||
Do not automatically attempt to mount any virtual file system
|
||||
(such as tracefs or BPF virtual file system) when necessary.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool map show**
|
||||
|
||||
::
|
||||
|
||||
10: hash name some_map flags 0x0
|
||||
key 4B value 8B max_entries 2048 memlock 167936B
|
||||
pids systemd(1)
|
||||
|
||||
The following three commands are equivalent:
|
||||
|
||||
|
|
||||
| **# bpftool map update id 10 key hex 20 c4 b7 00 value hex 0f ff ff ab 01 02 03 4c**
|
||||
| **# bpftool map update id 10 key 0x20 0xc4 0xb7 0x00 value 0x0f 0xff 0xff 0xab 0x01 0x02 0x03 0x4c**
|
||||
| **# bpftool map update id 10 key 32 196 183 0 value 15 255 255 171 1 2 3 76**
|
||||
|
||||
**# bpftool map lookup id 10 key 0 1 2 3**
|
||||
|
||||
::
|
||||
|
||||
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
|
||||
|
||||
|
||||
**# bpftool map dump id 10**
|
||||
|
||||
::
|
||||
|
||||
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
|
||||
key: 0d 00 07 00 value: 02 00 00 00 01 02 03 04
|
||||
Found 2 elements
|
||||
|
||||
**# bpftool map getnext id 10 key 0 1 2 3**
|
||||
|
||||
::
|
||||
|
||||
key:
|
||||
00 01 02 03
|
||||
next key:
|
||||
0d 00 07 00
|
||||
|
||||
|
|
||||
| **# mount -t bpf none /sys/fs/bpf/**
|
||||
| **# bpftool map pin id 10 /sys/fs/bpf/map**
|
||||
| **# bpftool map del pinned /sys/fs/bpf/map key 13 00 07 00**
|
||||
|
||||
Note that map update can also be used in order to change the program references
|
||||
hold by a program array map. This can be used, for example, to change the
|
||||
programs used for tail-call jumps at runtime, without having to reload the
|
||||
entry-point program. Below is an example for this use case: we load a program
|
||||
defining a prog array map, and with a main function that contains a tail call
|
||||
to other programs that can be used either to "process" packets or to "debug"
|
||||
processing. Note that the prog array map MUST be pinned into the BPF virtual
|
||||
file system for the map update to work successfully, as kernel flushes prog
|
||||
array maps when they have no more references from user space (and the update
|
||||
would be lost as soon as bpftool exits).
|
||||
|
||||
|
|
||||
| **# bpftool prog loadall tail_calls.o /sys/fs/bpf/foo type xdp**
|
||||
| **# bpftool prog --bpffs**
|
||||
|
||||
::
|
||||
|
||||
545: xdp name main_func tag 674b4b5597193dc3 gpl
|
||||
loaded_at 2018-12-12T15:02:58+0000 uid 0
|
||||
xlated 240B jited 257B memlock 4096B map_ids 294
|
||||
pinned /sys/fs/bpf/foo/xdp
|
||||
546: xdp name bpf_func_process tag e369a529024751fc gpl
|
||||
loaded_at 2018-12-12T15:02:58+0000 uid 0
|
||||
xlated 200B jited 164B memlock 4096B
|
||||
pinned /sys/fs/bpf/foo/process
|
||||
547: xdp name bpf_func_debug tag 0b597868bc7f0976 gpl
|
||||
loaded_at 2018-12-12T15:02:58+0000 uid 0
|
||||
xlated 200B jited 164B memlock 4096B
|
||||
pinned /sys/fs/bpf/foo/debug
|
||||
|
||||
**# bpftool map**
|
||||
|
||||
::
|
||||
|
||||
294: prog_array name jmp_table flags 0x0
|
||||
key 4B value 4B max_entries 1 memlock 4096B
|
||||
owner_prog_type xdp owner jited
|
||||
|
||||
|
|
||||
| **# bpftool map pin id 294 /sys/fs/bpf/bar**
|
||||
| **# bpftool map dump pinned /sys/fs/bpf/bar**
|
||||
|
||||
::
|
||||
|
||||
Found 0 elements
|
||||
|
||||
|
|
||||
| **# bpftool map update pinned /sys/fs/bpf/bar key 0 0 0 0 value pinned /sys/fs/bpf/foo/debug**
|
||||
| **# bpftool map dump pinned /sys/fs/bpf/bar**
|
||||
|
||||
::
|
||||
|
||||
key: 00 00 00 00 value: 22 02 00 00
|
||||
Found 1 element
|
||||
178
third_party/bpftool/docs/bpftool-net.rst
vendored
Normal file
178
third_party/bpftool/docs/bpftool-net.rst
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-net
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection of netdev/tc related bpf prog attachments
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **net** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **list** | **attach** | **detach** | **help** }
|
||||
|
||||
NET COMMANDS
|
||||
============
|
||||
|
||||
| **bpftool** **net** { **show** | **list** } [ **dev** *NAME* ]
|
||||
| **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overwrite** ]
|
||||
| **bpftool** **net detach** *ATTACH_TYPE* **dev** *NAME*
|
||||
| **bpftool** **net help**
|
||||
|
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
|
||||
| *ATTACH_TYPE* := { **xdp** | **xdpgeneric** | **xdpdrv** | **xdpoffload** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool net { show | list }** [ **dev** *NAME* ]
|
||||
List bpf program attachments in the kernel networking subsystem.
|
||||
|
||||
Currently, only device driver xdp attachments and tc filter
|
||||
classification/action attachments are implemented, i.e., for
|
||||
program types **BPF_PROG_TYPE_SCHED_CLS**,
|
||||
**BPF_PROG_TYPE_SCHED_ACT** and **BPF_PROG_TYPE_XDP**.
|
||||
For programs attached to a particular cgroup, e.g.,
|
||||
**BPF_PROG_TYPE_CGROUP_SKB**, **BPF_PROG_TYPE_CGROUP_SOCK**,
|
||||
**BPF_PROG_TYPE_SOCK_OPS** and **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**,
|
||||
users can use **bpftool cgroup** to dump cgroup attachments.
|
||||
For sk_{filter, skb, msg, reuseport} and lwt/seg6
|
||||
bpf programs, users should consult other tools, e.g., iproute2.
|
||||
|
||||
The current output will start with all xdp program attachments, followed by
|
||||
all tc class/qdisc bpf program attachments. Both xdp programs and
|
||||
tc programs are ordered based on ifindex number. If multiple bpf
|
||||
programs attached to the same networking device through **tc filter**,
|
||||
the order will be first all bpf programs attached to tc classes, then
|
||||
all bpf programs attached to non clsact qdiscs, and finally all
|
||||
bpf programs attached to root and clsact qdisc.
|
||||
|
||||
**bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overwrite** ]
|
||||
Attach bpf program *PROG* to network interface *NAME* with
|
||||
type specified by *ATTACH_TYPE*. Previously attached bpf program
|
||||
can be replaced by the command used with **overwrite** option.
|
||||
Currently, only XDP-related modes are supported for *ATTACH_TYPE*.
|
||||
|
||||
*ATTACH_TYPE* can be of:
|
||||
**xdp** - try native XDP and fallback to generic XDP if NIC driver does not support it;
|
||||
**xdpgeneric** - Generic XDP. runs at generic XDP hook when packet already enters receive path as skb;
|
||||
**xdpdrv** - Native XDP. runs earliest point in driver's receive path;
|
||||
**xdpoffload** - Offload XDP. runs directly on NIC on each packet reception;
|
||||
|
||||
**bpftool** **net detach** *ATTACH_TYPE* **dev** *NAME*
|
||||
Detach bpf program attached to network interface *NAME* with
|
||||
type specified by *ATTACH_TYPE*. To detach bpf program, same
|
||||
*ATTACH_TYPE* previously used for attach must be specified.
|
||||
Currently, only XDP-related modes are supported for *ATTACH_TYPE*.
|
||||
|
||||
**bpftool net help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
||||
| **# bpftool net**
|
||||
|
||||
::
|
||||
|
||||
xdp:
|
||||
eth0(2) driver id 198
|
||||
|
||||
tc:
|
||||
eth0(2) htb name prefix_matcher.o:[cls_prefix_matcher_htb] id 111727 act []
|
||||
eth0(2) clsact/ingress fbflow_icmp id 130246 act []
|
||||
eth0(2) clsact/egress prefix_matcher.o:[cls_prefix_matcher_clsact] id 111726
|
||||
eth0(2) clsact/egress cls_fg_dscp id 108619 act []
|
||||
eth0(2) clsact/egress fbflow_egress id 130245
|
||||
|
||||
|
|
||||
| **# bpftool -jp net**
|
||||
|
||||
::
|
||||
|
||||
[{
|
||||
"xdp": [{
|
||||
"devname": "eth0",
|
||||
"ifindex": 2,
|
||||
"mode": "driver",
|
||||
"id": 198
|
||||
}
|
||||
],
|
||||
"tc": [{
|
||||
"devname": "eth0",
|
||||
"ifindex": 2,
|
||||
"kind": "htb",
|
||||
"name": "prefix_matcher.o:[cls_prefix_matcher_htb]",
|
||||
"id": 111727,
|
||||
"act": []
|
||||
},{
|
||||
"devname": "eth0",
|
||||
"ifindex": 2,
|
||||
"kind": "clsact/ingress",
|
||||
"name": "fbflow_icmp",
|
||||
"id": 130246,
|
||||
"act": []
|
||||
},{
|
||||
"devname": "eth0",
|
||||
"ifindex": 2,
|
||||
"kind": "clsact/egress",
|
||||
"name": "prefix_matcher.o:[cls_prefix_matcher_clsact]",
|
||||
"id": 111726,
|
||||
},{
|
||||
"devname": "eth0",
|
||||
"ifindex": 2,
|
||||
"kind": "clsact/egress",
|
||||
"name": "cls_fg_dscp",
|
||||
"id": 108619,
|
||||
"act": []
|
||||
},{
|
||||
"devname": "eth0",
|
||||
"ifindex": 2,
|
||||
"kind": "clsact/egress",
|
||||
"name": "fbflow_egress",
|
||||
"id": 130245,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
||||
| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
|
||||
| **# bpftool net**
|
||||
|
||||
::
|
||||
|
||||
xdp:
|
||||
enp6s0np0(4) driver id 16
|
||||
|
||||
|
|
||||
| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
|
||||
| **# bpftool net attach xdpdrv id 20 dev enp6s0np0 overwrite**
|
||||
| **# bpftool net**
|
||||
|
||||
::
|
||||
|
||||
xdp:
|
||||
enp6s0np0(4) driver id 20
|
||||
|
||||
|
|
||||
| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
|
||||
| **# bpftool net detach xdpdrv dev enp6s0np0**
|
||||
| **# bpftool net**
|
||||
|
||||
::
|
||||
|
||||
xdp:
|
||||
69
third_party/bpftool/docs/bpftool-perf.rst
vendored
Normal file
69
third_party/bpftool/docs/bpftool-perf.rst
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-perf
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection of perf related bpf prog attachments
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **perf** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **list** | **help** }
|
||||
|
||||
PERF COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **perf** { **show** | **list** }
|
||||
| **bpftool** **perf help**
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool perf { show | list }**
|
||||
List all raw_tracepoint, tracepoint, kprobe attachment in the system.
|
||||
|
||||
Output will start with process id and file descriptor in that process,
|
||||
followed by bpf program id, attachment information, and attachment point.
|
||||
The attachment point for raw_tracepoint/tracepoint is the trace probe name.
|
||||
The attachment point for k[ret]probe is either symbol name and offset,
|
||||
or a kernel virtual address.
|
||||
The attachment point for u[ret]probe is the file name and the file offset.
|
||||
|
||||
**bpftool perf help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
||||
| **# bpftool perf**
|
||||
|
||||
::
|
||||
|
||||
pid 21711 fd 5: prog_id 5 kprobe func __x64_sys_write offset 0
|
||||
pid 21765 fd 5: prog_id 7 kretprobe func __x64_sys_nanosleep offset 0
|
||||
pid 21767 fd 5: prog_id 8 tracepoint sys_enter_nanosleep
|
||||
pid 21800 fd 5: prog_id 9 uprobe filename /home/yhs/a.out offset 1159
|
||||
|
||||
|
|
||||
| **# bpftool -j perf**
|
||||
|
||||
::
|
||||
|
||||
[{"pid":21711,"fd":5,"prog_id":5,"fd_type":"kprobe","func":"__x64_sys_write","offset":0}, \
|
||||
{"pid":21765,"fd":5,"prog_id":7,"fd_type":"kretprobe","func":"__x64_sys_nanosleep","offset":0}, \
|
||||
{"pid":21767,"fd":5,"prog_id":8,"fd_type":"tracepoint","tracepoint":"sys_enter_nanosleep"}, \
|
||||
{"pid":21800,"fd":5,"prog_id":9,"fd_type":"uprobe","filename":"/home/yhs/a.out","offset":1159}]
|
||||
375
third_party/bpftool/docs/bpftool-prog.rst
vendored
Normal file
375
third_party/bpftool/docs/bpftool-prog.rst
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
bpftool-prog
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF progs
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **prog** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| |
|
||||
{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
|
||||
{ **-L** | **--use-loader** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
|
||||
**loadall** | **help** }
|
||||
|
||||
PROG COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **prog** { **show** | **list** } [*PROG*]
|
||||
| **bpftool** **prog dump xlated** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] [**visual**] }]
|
||||
| **bpftool** **prog dump jited** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] }]
|
||||
| **bpftool** **prog pin** *PROG* *FILE*
|
||||
| **bpftool** **prog** { **load** | **loadall** } *OBJ* *PATH* [**type** *TYPE*] [**map** { **idx** *IDX* | **name** *NAME* } *MAP*] [{ **offload_dev** | **xdpmeta_dev** } *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**]
|
||||
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
|
||||
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
|
||||
| **bpftool** **prog tracelog**
|
||||
| **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
|
||||
| **bpftool** **prog profile** *PROG* [**duration** *DURATION*] *METRICs*
|
||||
| **bpftool** **prog help**
|
||||
|
|
||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
|
||||
| *TYPE* := {
|
||||
| **socket** | **kprobe** | **kretprobe** | **classifier** | **action** |
|
||||
| **tracepoint** | **raw_tracepoint** | **xdp** | **perf_event** | **cgroup/skb** |
|
||||
| **cgroup/sock** | **cgroup/dev** | **lwt_in** | **lwt_out** | **lwt_xmit** |
|
||||
| **lwt_seg6local** | **sockops** | **sk_skb** | **sk_msg** | **lirc_mode2** |
|
||||
| **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** |
|
||||
| **cgroup/connect4** | **cgroup/connect6** | **cgroup/getpeername4** | **cgroup/getpeername6** |
|
||||
| **cgroup/getsockname4** | **cgroup/getsockname6** | **cgroup/sendmsg4** | **cgroup/sendmsg6** |
|
||||
| **cgroup/recvmsg4** | **cgroup/recvmsg6** | **cgroup/sysctl** |
|
||||
| **cgroup/getsockopt** | **cgroup/setsockopt** | **cgroup/sock_release** |
|
||||
| **struct_ops** | **fentry** | **fexit** | **freplace** | **sk_lookup**
|
||||
| }
|
||||
| *ATTACH_TYPE* := {
|
||||
| **sk_msg_verdict** | **sk_skb_verdict** | **sk_skb_stream_verdict** |
|
||||
| **sk_skb_stream_parser** | **flow_dissector**
|
||||
| }
|
||||
| *METRICs* := {
|
||||
| **cycles** | **instructions** | **l1d_loads** | **llc_misses** |
|
||||
| **itlb_misses** | **dtlb_misses**
|
||||
| }
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool prog { show | list }** [*PROG*]
|
||||
Show information about loaded programs. If *PROG* is
|
||||
specified show information only about given programs,
|
||||
otherwise list all programs currently loaded on the system.
|
||||
In case of **tag** or **name**, *PROG* may match several
|
||||
programs which will all be shown.
|
||||
|
||||
Output will start with program ID followed by program type and
|
||||
zero or more named attributes (depending on kernel version).
|
||||
|
||||
Since Linux 5.1 the kernel can collect statistics on BPF
|
||||
programs (such as the total time spent running the program,
|
||||
and the number of times it was run). If available, bpftool
|
||||
shows such statistics. However, the kernel does not collect
|
||||
them by defaults, as it slightly impacts performance on each
|
||||
program run. Activation or deactivation of the feature is
|
||||
performed via the **kernel.bpf_stats_enabled** sysctl knob.
|
||||
|
||||
Since Linux 5.8 bpftool is able to discover information about
|
||||
processes that hold open file descriptors (FDs) against BPF
|
||||
programs. On such kernels bpftool will automatically emit this
|
||||
information as well.
|
||||
|
||||
**bpftool prog dump xlated** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] [**visual**] }]
|
||||
Dump eBPF instructions of the programs from the kernel. By
|
||||
default, eBPF will be disassembled and printed to standard
|
||||
output in human-readable format. In this case, **opcodes**
|
||||
controls if raw opcodes should be printed as well.
|
||||
|
||||
In case of **tag** or **name**, *PROG* may match several
|
||||
programs which will all be dumped. However, if **file** or
|
||||
**visual** is specified, *PROG* must match a single program.
|
||||
|
||||
If **file** is specified, the binary image will instead be
|
||||
written to *FILE*.
|
||||
|
||||
If **visual** is specified, control flow graph (CFG) will be
|
||||
built instead, and eBPF instructions will be presented with
|
||||
CFG in DOT format, on standard output.
|
||||
|
||||
If the programs have line_info available, the source line will
|
||||
be displayed. If **linum** is specified, the filename, line
|
||||
number and line column will also be displayed.
|
||||
|
||||
**bpftool prog dump jited** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] }]
|
||||
Dump jited image (host machine code) of the program.
|
||||
|
||||
If *FILE* is specified image will be written to a file,
|
||||
otherwise it will be disassembled and printed to stdout.
|
||||
*PROG* must match a single program when **file** is specified.
|
||||
|
||||
**opcodes** controls if raw opcodes will be printed.
|
||||
|
||||
If the prog has line_info available, the source line will
|
||||
be displayed. If **linum** is specified, the filename, line
|
||||
number and line column will also be displayed.
|
||||
|
||||
**bpftool prog pin** *PROG* *FILE*
|
||||
Pin program *PROG* as *FILE*.
|
||||
|
||||
Note: *FILE* must be located in *bpffs* mount. It must not
|
||||
contain a dot character ('.'), which is reserved for future
|
||||
extensions of *bpffs*.
|
||||
|
||||
**bpftool prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** { **idx** *IDX* | **name** *NAME* } *MAP*] [{ **offload_dev** | **xdpmeta_dev** } *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**]
|
||||
Load bpf program(s) from binary *OBJ* and pin as *PATH*.
|
||||
**bpftool prog load** pins only the first program from the
|
||||
*OBJ* as *PATH*. **bpftool prog loadall** pins all programs
|
||||
from the *OBJ* under *PATH* directory.
|
||||
**type** is optional, if not specified program type will be
|
||||
inferred from section names.
|
||||
By default bpftool will create new maps as declared in the ELF
|
||||
object being loaded. **map** parameter allows for the reuse
|
||||
of existing maps. It can be specified multiple times, each
|
||||
time for a different map. *IDX* refers to index of the map
|
||||
to be replaced in the ELF file counting from 0, while *NAME*
|
||||
allows to replace a map by name. *MAP* specifies the map to
|
||||
use, referring to it by **id** or through a **pinned** file.
|
||||
If **offload_dev** *NAME* is specified program will be loaded
|
||||
onto given networking device (offload).
|
||||
If **xdpmeta_dev** *NAME* is specified program will become
|
||||
device-bound without offloading, this facilitates access
|
||||
to XDP metadata.
|
||||
Optional **pinmaps** argument can be provided to pin all
|
||||
maps under *MAP_DIR* directory.
|
||||
|
||||
If **autoattach** is specified program will be attached
|
||||
before pin. In that case, only the link (representing the
|
||||
program attached to its hook) is pinned, not the program as
|
||||
such, so the path won't show in **bpftool prog show -f**,
|
||||
only show in **bpftool link show -f**. Also, this only works
|
||||
when bpftool (libbpf) is able to infer all necessary
|
||||
information from the object file, in particular, it's not
|
||||
supported for all program types. If a program does not
|
||||
support autoattach, bpftool falls back to regular pinning
|
||||
for that program instead.
|
||||
|
||||
Note: *PATH* must be located in *bpffs* mount. It must not
|
||||
contain a dot character ('.'), which is reserved for future
|
||||
extensions of *bpffs*.
|
||||
|
||||
**bpftool prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
|
||||
Attach bpf program *PROG* (with type specified by
|
||||
*ATTACH_TYPE*). Most *ATTACH_TYPEs* require a *MAP*
|
||||
parameter, with the exception of *flow_dissector* which is
|
||||
attached to current networking name space.
|
||||
|
||||
**bpftool prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
|
||||
Detach bpf program *PROG* (with type specified by
|
||||
*ATTACH_TYPE*). Most *ATTACH_TYPEs* require a *MAP*
|
||||
parameter, with the exception of *flow_dissector* which is
|
||||
detached from the current networking name space.
|
||||
|
||||
**bpftool prog tracelog**
|
||||
Dump the trace pipe of the system to the console (stdout).
|
||||
Hit <Ctrl+C> to stop printing. BPF programs can write to this
|
||||
trace pipe at runtime with the **bpf_trace_printk**\ () helper.
|
||||
This should be used only for debugging purposes. For
|
||||
streaming data from BPF programs to user space, one can use
|
||||
perf events (see also **bpftool-map**\ (8)).
|
||||
|
||||
**bpftool prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
|
||||
Run BPF program *PROG* in the kernel testing infrastructure
|
||||
for BPF, meaning that the program works on the data and
|
||||
context provided by the user, and not on actual packets or
|
||||
monitored functions etc. Return value and duration for the
|
||||
test run are printed out to the console.
|
||||
|
||||
Input data is read from the *FILE* passed with **data_in**.
|
||||
If this *FILE* is "**-**", input data is read from standard
|
||||
input. Input context, if any, is read from *FILE* passed with
|
||||
**ctx_in**. Again, "**-**" can be used to read from standard
|
||||
input, but only if standard input is not already in use for
|
||||
input data. If a *FILE* is passed with **data_out**, output
|
||||
data is written to that file. Similarly, output context is
|
||||
written to the *FILE* passed with **ctx_out**. For both
|
||||
output flows, "**-**" can be used to print to the standard
|
||||
output (as plain text, or JSON if relevant option was
|
||||
passed). If output keywords are omitted, output data and
|
||||
context are discarded. Keywords **data_size_out** and
|
||||
**ctx_size_out** are used to pass the size (in bytes) for the
|
||||
output buffers to the kernel, although the default of 32 kB
|
||||
should be more than enough for most cases.
|
||||
|
||||
Keyword **repeat** is used to indicate the number of
|
||||
consecutive runs to perform. Note that output data and
|
||||
context printed to files correspond to the last of those
|
||||
runs. The duration printed out at the end of the runs is an
|
||||
average over all runs performed by the command.
|
||||
|
||||
Not all program types support test run. Among those which do,
|
||||
not all of them can take the **ctx_in**/**ctx_out**
|
||||
arguments. bpftool does not perform checks on program types.
|
||||
|
||||
**bpftool prog profile** *PROG* [**duration** *DURATION*] *METRICs*
|
||||
Profile *METRICs* for bpf program *PROG* for *DURATION*
|
||||
seconds or until user hits <Ctrl+C>. *DURATION* is optional.
|
||||
If *DURATION* is not specified, the profiling will run up to
|
||||
**UINT_MAX** seconds.
|
||||
|
||||
**bpftool prog help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
-f, --bpffs
|
||||
When showing BPF programs, show file names of pinned
|
||||
programs.
|
||||
|
||||
-m, --mapcompat
|
||||
Allow loading maps with unknown map definitions.
|
||||
|
||||
-n, --nomount
|
||||
Do not automatically attempt to mount any virtual file system
|
||||
(such as tracefs or BPF virtual file system) when necessary.
|
||||
|
||||
-L, --use-loader
|
||||
Load program as a "loader" program. This is useful to debug
|
||||
the generation of such programs. When this option is in
|
||||
use, bpftool attempts to load the programs from the object
|
||||
file into the kernel, but does not pin them (therefore, the
|
||||
*PATH* must not be provided).
|
||||
|
||||
When combined with the **-d**\ \|\ **--debug** option,
|
||||
additional debug messages are generated, and the execution
|
||||
of the loader program will use the **bpf_trace_printk**\ ()
|
||||
helper to log each step of loading BTF, creating the maps,
|
||||
and loading the programs (see **bpftool prog tracelog** as
|
||||
a way to dump those messages).
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool prog show**
|
||||
|
||||
::
|
||||
|
||||
10: xdp name some_prog tag 005a3d2123620c8b gpl run_time_ns 81632 run_cnt 10
|
||||
loaded_at 2017-09-29T20:11:00+0000 uid 0
|
||||
xlated 528B jited 370B memlock 4096B map_ids 10
|
||||
pids systemd(1)
|
||||
|
||||
**# bpftool --json --pretty prog show**
|
||||
|
||||
::
|
||||
|
||||
[{
|
||||
"id": 10,
|
||||
"type": "xdp",
|
||||
"tag": "005a3d2123620c8b",
|
||||
"gpl_compatible": true,
|
||||
"run_time_ns": 81632,
|
||||
"run_cnt": 10,
|
||||
"loaded_at": 1506715860,
|
||||
"uid": 0,
|
||||
"bytes_xlated": 528,
|
||||
"jited": true,
|
||||
"bytes_jited": 370,
|
||||
"bytes_memlock": 4096,
|
||||
"map_ids": [10
|
||||
],
|
||||
"pids": [{
|
||||
"pid": 1,
|
||||
"comm": "systemd"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
||||
| **# bpftool prog dump xlated id 10 file /tmp/t**
|
||||
| **$ ls -l /tmp/t**
|
||||
|
||||
::
|
||||
|
||||
-rw------- 1 root root 560 Jul 22 01:42 /tmp/t
|
||||
|
||||
**# bpftool prog dump jited tag 005a3d2123620c8b**
|
||||
|
||||
::
|
||||
|
||||
0: push %rbp
|
||||
1: mov %rsp,%rbp
|
||||
2: sub $0x228,%rsp
|
||||
3: sub $0x28,%rbp
|
||||
4: mov %rbx,0x0(%rbp)
|
||||
|
||||
|
|
||||
| **# mount -t bpf none /sys/fs/bpf/**
|
||||
| **# bpftool prog pin id 10 /sys/fs/bpf/prog**
|
||||
| **# bpftool prog load ./my_prog.o /sys/fs/bpf/prog2**
|
||||
| **# ls -l /sys/fs/bpf/**
|
||||
|
||||
::
|
||||
|
||||
-rw------- 1 root root 0 Jul 22 01:43 prog
|
||||
-rw------- 1 root root 0 Jul 22 01:44 prog2
|
||||
|
||||
**# bpftool prog dump jited pinned /sys/fs/bpf/prog opcodes**
|
||||
|
||||
::
|
||||
|
||||
0: push %rbp
|
||||
55
|
||||
1: mov %rsp,%rbp
|
||||
48 89 e5
|
||||
4: sub $0x228,%rsp
|
||||
48 81 ec 28 02 00 00
|
||||
b: sub $0x28,%rbp
|
||||
48 83 ed 28
|
||||
f: mov %rbx,0x0(%rbp)
|
||||
48 89 5d 00
|
||||
|
||||
|
|
||||
| **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7**
|
||||
| **# bpftool prog show pinned /sys/fs/bpf/xdp1**
|
||||
|
||||
::
|
||||
|
||||
9: xdp name xdp_prog1 tag 539ec6ce11b52f98 gpl
|
||||
loaded_at 2018-06-25T16:17:31-0700 uid 0
|
||||
xlated 488B jited 336B memlock 4096B map_ids 7
|
||||
|
||||
**# rm /sys/fs/bpf/xdp1**
|
||||
|
||||
|
|
||||
| **# bpftool prog profile id 337 duration 10 cycles instructions llc_misses**
|
||||
|
||||
::
|
||||
|
||||
51397 run_cnt
|
||||
40176203 cycles (83.05%)
|
||||
42518139 instructions # 1.06 insns per cycle (83.39%)
|
||||
123 llc_misses # 2.89 LLC misses per million insns (83.15%)
|
||||
|
||||
|
|
||||
| Output below is for the trace logs.
|
||||
| Run in separate terminals:
|
||||
| **# bpftool prog tracelog**
|
||||
| **# bpftool prog load -L -d file.o**
|
||||
|
||||
::
|
||||
|
||||
bpftool-620059 [004] d... 2634685.517903: bpf_trace_printk: btf_load size 665 r=5
|
||||
bpftool-620059 [004] d... 2634685.517912: bpf_trace_printk: map_create sample_map idx 0 type 2 value_size 4 value_btf_id 0 r=6
|
||||
bpftool-620059 [004] d... 2634685.517997: bpf_trace_printk: prog_load sample insn_cnt 13 r=7
|
||||
bpftool-620059 [004] d... 2634685.517999: bpf_trace_printk: close(5) = 0
|
||||
92
third_party/bpftool/docs/bpftool-struct_ops.rst
vendored
Normal file
92
third_party/bpftool/docs/bpftool-struct_ops.rst
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
==================
|
||||
bpftool-struct_ops
|
||||
==================
|
||||
-------------------------------------------------------------------------------
|
||||
tool to register/unregister/introspect BPF struct_ops
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **struct_ops** *COMMAND*
|
||||
|
||||
*OPTIONS* := { |COMMON_OPTIONS| }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **list** | **dump** | **register** | **unregister** | **help** }
|
||||
|
||||
STRUCT_OPS COMMANDS
|
||||
===================
|
||||
|
||||
| **bpftool** **struct_ops { show | list }** [*STRUCT_OPS_MAP*]
|
||||
| **bpftool** **struct_ops dump** [*STRUCT_OPS_MAP*]
|
||||
| **bpftool** **struct_ops register** *OBJ* [*LINK_DIR*]
|
||||
| **bpftool** **struct_ops unregister** *STRUCT_OPS_MAP*
|
||||
| **bpftool** **struct_ops help**
|
||||
|
|
||||
| *STRUCT_OPS_MAP* := { **id** *STRUCT_OPS_MAP_ID* | **name** *STRUCT_OPS_MAP_NAME* }
|
||||
| *OBJ* := /a/file/of/bpf_struct_ops.o
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool struct_ops { show | list }** [*STRUCT_OPS_MAP*]
|
||||
Show brief information about the struct_ops in the system.
|
||||
If *STRUCT_OPS_MAP* is specified, it shows information only
|
||||
for the given struct_ops. Otherwise, it lists all struct_ops
|
||||
currently existing in the system.
|
||||
|
||||
Output will start with struct_ops map ID, followed by its map
|
||||
name and its struct_ops's kernel type.
|
||||
|
||||
**bpftool struct_ops dump** [*STRUCT_OPS_MAP*]
|
||||
Dump details information about the struct_ops in the system.
|
||||
If *STRUCT_OPS_MAP* is specified, it dumps information only
|
||||
for the given struct_ops. Otherwise, it dumps all struct_ops
|
||||
currently existing in the system.
|
||||
|
||||
**bpftool struct_ops register** *OBJ* [*LINK_DIR*]
|
||||
Register bpf struct_ops from *OBJ*. All struct_ops under
|
||||
the ELF section ".struct_ops" and ".struct_ops.link" will
|
||||
be registered to its kernel subsystem. For each
|
||||
struct_ops in the ".struct_ops.link" section, a link
|
||||
will be created. You can give *LINK_DIR* to provide a
|
||||
directory path where these links will be pinned with the
|
||||
same name as their corresponding map name.
|
||||
|
||||
**bpftool struct_ops unregister** *STRUCT_OPS_MAP*
|
||||
Unregister the *STRUCT_OPS_MAP* from the kernel subsystem.
|
||||
|
||||
**bpftool struct_ops help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool struct_ops show**
|
||||
|
||||
::
|
||||
|
||||
100: dctcp tcp_congestion_ops
|
||||
105: cubic tcp_congestion_ops
|
||||
|
||||
**# bpftool struct_ops unregister id 105**
|
||||
|
||||
::
|
||||
|
||||
Unregistered tcp_congestion_ops cubic id 105
|
||||
|
||||
**# bpftool struct_ops register bpf_cubic.o**
|
||||
|
||||
::
|
||||
|
||||
Registered tcp_congestion_ops cubic id 110
|
||||
70
third_party/bpftool/docs/bpftool.rst
vendored
Normal file
70
third_party/bpftool/docs/bpftool.rst
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
================
|
||||
BPFTOOL
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF programs and maps
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
.. include:: substitutions.rst
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] *OBJECT* { *COMMAND* | **help** }
|
||||
|
||||
**bpftool** **batch file** *FILE*
|
||||
|
||||
**bpftool** **version**
|
||||
|
||||
*OBJECT* := { **map** | **program** | **link** | **cgroup** | **perf** | **net** | **feature** |
|
||||
**btf** | **gen** | **struct_ops** | **iter** }
|
||||
|
||||
*OPTIONS* := { { **-V** | **--version** } | |COMMON_OPTIONS| }
|
||||
|
||||
*MAP-COMMANDS* :=
|
||||
{ **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext** |
|
||||
**delete** | **pin** | **event_pipe** | **help** }
|
||||
|
||||
*PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin** |
|
||||
**load** | **attach** | **detach** | **help** }
|
||||
|
||||
*LINK-COMMANDS* := { **show** | **list** | **pin** | **detach** | **help** }
|
||||
|
||||
*CGROUP-COMMANDS* := { **show** | **list** | **attach** | **detach** | **help** }
|
||||
|
||||
*PERF-COMMANDS* := { **show** | **list** | **help** }
|
||||
|
||||
*NET-COMMANDS* := { **show** | **list** | **help** }
|
||||
|
||||
*FEATURE-COMMANDS* := { **probe** | **help** }
|
||||
|
||||
*BTF-COMMANDS* := { **show** | **list** | **dump** | **help** }
|
||||
|
||||
*GEN-COMMANDS* := { **object** | **skeleton** | **min_core_btf** | **help** }
|
||||
|
||||
*STRUCT-OPS-COMMANDS* := { **show** | **list** | **dump** | **register** | **unregister** | **help** }
|
||||
|
||||
*ITER-COMMANDS* := { **pin** | **help** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
*bpftool* allows for inspection and simple modification of BPF objects
|
||||
on the system.
|
||||
|
||||
Note that format of the output of all tools is not guaranteed to be
|
||||
stable and should not be depended upon.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
.. include:: common_options.rst
|
||||
|
||||
-m, --mapcompat
|
||||
Allow loading maps with unknown map definitions.
|
||||
|
||||
-n, --nomount
|
||||
Do not automatically attempt to mount any virtual file system
|
||||
(such as tracefs or BPF virtual file system) when necessary.
|
||||
25
third_party/bpftool/docs/common_options.rst
vendored
Normal file
25
third_party/bpftool/docs/common_options.rst
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
-h, --help
|
||||
Print short help message (similar to **bpftool help**).
|
||||
|
||||
-V, --version
|
||||
Print bpftool's version number (similar to **bpftool version**), the
|
||||
number of the libbpf version in use, and optional features that were
|
||||
included when bpftool was compiled. Optional features include linking
|
||||
against LLVM or libbfd to provide the disassembler for JIT-ted
|
||||
programs (**bpftool prog dump jited**) and usage of BPF skeletons
|
||||
(some features like **bpftool prog profile** or showing pids
|
||||
associated to BPF objects may rely on it).
|
||||
|
||||
-j, --json
|
||||
Generate JSON output. For commands that cannot produce JSON, this
|
||||
option has no effect.
|
||||
|
||||
-p, --pretty
|
||||
Generate human-readable JSON output. Implies **-j**.
|
||||
|
||||
-d, --debug
|
||||
Print all logs available, even debug-level information. This includes
|
||||
logs from libbpf as well as from the verifier, when attempting to
|
||||
load programs.
|
||||
3
third_party/bpftool/docs/substitutions.rst
vendored
Normal file
3
third_party/bpftool/docs/substitutions.rst
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
.. |COMMON_OPTIONS| replace:: { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-d** | **--debug** }
|
||||
12
third_party/bpftool/include/linux/bitops.h
vendored
Normal file
12
third_party/bpftool/include/linux/bitops.h
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef _LINUX_BITOPS_H_
|
||||
#define _LINUX_BITOPS_H_
|
||||
|
||||
#ifndef BITS_PER_LONG
|
||||
# define BITS_PER_LONG __WORDSIZE
|
||||
#endif
|
||||
|
||||
#define BITS_PER_BYTE 8
|
||||
|
||||
#endif
|
||||
45
third_party/bpftool/include/linux/build_bug.h
vendored
Normal file
45
third_party/bpftool/include/linux/build_bug.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef _LINUX_BUILD_BUG_H
|
||||
#define _LINUX_BUILD_BUG_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/*
|
||||
* Force a compilation error if condition is true, but also produce a
|
||||
* result (of value 0 and type int), so the expression can be used
|
||||
* e.g. in a structure initializer (or where-ever else comma expressions
|
||||
* aren't permitted).
|
||||
*/
|
||||
#define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); })))
|
||||
|
||||
/**
|
||||
* BUILD_BUG_ON_MSG - break compile if a condition is true & emit supplied
|
||||
* error message.
|
||||
* @condition: the condition which the compiler should know is false.
|
||||
*
|
||||
* See BUILD_BUG_ON for description.
|
||||
*/
|
||||
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
|
||||
|
||||
/**
|
||||
* BUILD_BUG_ON - break compile if a condition is true.
|
||||
* @condition: the condition which the compiler should know is false.
|
||||
*
|
||||
* If you have some code which relies on certain constants being equal, or
|
||||
* some other compile-time-evaluated condition, you should use BUILD_BUG_ON to
|
||||
* detect if someone changes it.
|
||||
*/
|
||||
#define BUILD_BUG_ON(condition) \
|
||||
BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
|
||||
|
||||
/**
|
||||
* BUILD_BUG - break compile if used.
|
||||
*
|
||||
* If you have some code that you expect the compiler to eliminate at
|
||||
* build time, you should use BUILD_BUG to detect if it is
|
||||
* unexpectedly used.
|
||||
*/
|
||||
#define BUILD_BUG() BUILD_BUG_ON_MSG(1, "BUILD_BUG failed")
|
||||
|
||||
#endif
|
||||
29
third_party/bpftool/include/linux/compiler-gcc.h
vendored
Normal file
29
third_party/bpftool/include/linux/compiler-gcc.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef _LINUX_COMPILER_H_
|
||||
#error "Please don't include <linux/compiler-gcc.h> directly, include <linux/compiler.h> instead."
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Common definitions for all gcc versions go here.
|
||||
*/
|
||||
#ifndef GCC_VERSION
|
||||
#define GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
#endif
|
||||
|
||||
#if GCC_VERSION >= 70000 && !defined(__CHECKER__)
|
||||
# define __fallthrough __attribute__ ((fallthrough))
|
||||
#endif
|
||||
|
||||
#if __has_attribute(__error__)
|
||||
# define __compiletime_error(message) __attribute__((error(message)))
|
||||
#endif
|
||||
|
||||
/* &a[0] degrades to a pointer: a different type from an array */
|
||||
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
|
||||
#ifndef __noreturn
|
||||
#define __noreturn __attribute__((noreturn))
|
||||
#endif
|
||||
#define __printf(a, b) __attribute__((format(printf, a, b)))
|
||||
51
third_party/bpftool/include/linux/compiler.h
vendored
Normal file
51
third_party/bpftool/include/linux/compiler.h
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef _LINUX_COMPILER_H_
|
||||
#define _LINUX_COMPILER_H_
|
||||
|
||||
#include <linux/compiler_types.h>
|
||||
|
||||
#if defined(__OPTIMIZE__) && __has_attribute(__error__)
|
||||
# define __compiletime_assert(condition, msg, prefix, suffix) \
|
||||
do { \
|
||||
extern void prefix ## suffix(void) __compiletime_error(msg); \
|
||||
if (!(condition)) \
|
||||
prefix ## suffix(); \
|
||||
} while (0)
|
||||
#else
|
||||
# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define _compiletime_assert(condition, msg, prefix, suffix) \
|
||||
__compiletime_assert(condition, msg, prefix, suffix)
|
||||
|
||||
/**
|
||||
* compiletime_assert - break build and emit msg if condition is false
|
||||
* @condition: a compile-time constant condition to check
|
||||
* @msg: a message to emit if condition is false
|
||||
*
|
||||
* In tradition of POSIX assert, this macro will break the build if the
|
||||
* supplied condition is *false*, emitting the supplied error message if the
|
||||
* compiler has support to do so.
|
||||
*/
|
||||
#define compiletime_assert(condition, msg) \
|
||||
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
|
||||
|
||||
/* Are two types/vars the same type (ignoring qualifiers)? */
|
||||
#ifndef __same_type
|
||||
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
|
||||
#endif
|
||||
|
||||
#ifndef __maybe_unused
|
||||
# define __maybe_unused __attribute__((unused))
|
||||
#endif
|
||||
|
||||
#ifndef __weak
|
||||
# define __weak __attribute__((weak))
|
||||
#endif
|
||||
|
||||
#ifndef __fallthrough
|
||||
# define __fallthrough
|
||||
#endif
|
||||
|
||||
#endif
|
||||
11
third_party/bpftool/include/linux/compiler_types.h
vendored
Normal file
11
third_party/bpftool/include/linux/compiler_types.h
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_COMPILER_TYPES_H
|
||||
#define __LINUX_COMPILER_TYPES_H
|
||||
|
||||
/* Compiler specific macros. */
|
||||
#ifdef __GNUC__
|
||||
#include <linux/compiler-gcc.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
33
third_party/bpftool/include/linux/err.h
vendored
Normal file
33
third_party/bpftool/include/linux/err.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_ERR_H
|
||||
#define __LINUX_ERR_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/errno.h>
|
||||
|
||||
#define MAX_ERRNO 4095
|
||||
|
||||
#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
|
||||
|
||||
static inline void * ERR_PTR(long error_)
|
||||
{
|
||||
return (void *) error_;
|
||||
}
|
||||
|
||||
static inline long PTR_ERR(const void *ptr)
|
||||
{
|
||||
return (long) ptr;
|
||||
}
|
||||
|
||||
static inline bool IS_ERR(const void *ptr)
|
||||
{
|
||||
return IS_ERR_VALUE((unsigned long)ptr);
|
||||
}
|
||||
|
||||
static inline bool IS_ERR_OR_NULL(const void *ptr)
|
||||
{
|
||||
return (!ptr) || IS_ERR_VALUE((unsigned long)ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
56
third_party/bpftool/include/linux/filter.h
vendored
Normal file
56
third_party/bpftool/include/linux/filter.h
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_FILTER_H
|
||||
#define __LINUX_FILTER_H
|
||||
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#define BPF_ALU64_IMM(OP, DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_MOV64_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_EXIT_INSN() \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_EXIT, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_EMIT_CALL(FUNC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_CALL, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = ((FUNC) - BPF_FUNC_unspec) })
|
||||
|
||||
#endif
|
||||
35
third_party/bpftool/include/linux/kernel.h
vendored
Normal file
35
third_party/bpftool/include/linux/kernel.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_KERNEL_H
|
||||
#define __LINUX_KERNEL_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof(((type *)0)->member) * __mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); })
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(x, y) ({ \
|
||||
typeof(x) _max1 = (x); \
|
||||
typeof(y) _max2 = (y); \
|
||||
(void) (&_max1 == &_max2); \
|
||||
_max1 > _max2 ? _max1 : _max2; })
|
||||
#endif
|
||||
|
||||
#ifndef roundup
|
||||
#define roundup(x, y) ( \
|
||||
{ \
|
||||
const typeof(y) __y = y; \
|
||||
(((x) + (__y - 1)) / __y) * __y; \
|
||||
} \
|
||||
)
|
||||
#endif
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
|
||||
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
|
||||
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
|
||||
|
||||
#endif
|
||||
119
third_party/bpftool/include/linux/list.h
vendored
Normal file
119
third_party/bpftool/include/linux/list.h
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_LIST_H
|
||||
#define __LINUX_LIST_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
#define POISON_POINTER_DELTA 0
|
||||
#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA)
|
||||
#define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA)
|
||||
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
* Note: list_empty() on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
static inline void __list_del_entry(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = LIST_POISON1;
|
||||
entry->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
#define list_last_entry(ptr, type, member) \
|
||||
list_entry((ptr)->prev, type, member)
|
||||
#define list_next_entry(pos, member) \
|
||||
list_entry((pos)->member.next, typeof(*(pos)), member)
|
||||
#define list_prev_entry(pos, member) \
|
||||
list_entry((pos)->member.prev, typeof(*(pos)), member)
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_first_entry(head, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_next_entry(pos, member))
|
||||
#define list_for_each_entry_from(pos, head, member) \
|
||||
for (; &pos->member != (head); \
|
||||
pos = list_next_entry(pos, member))
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_first_entry(head, typeof(*pos), member), \
|
||||
n = list_next_entry(pos, member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_next_entry(n, member))
|
||||
|
||||
#endif
|
||||
8
third_party/bpftool/include/linux/sizes.h
vendored
Normal file
8
third_party/bpftool/include/linux/sizes.h
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_SIZES_H__
|
||||
#define __LINUX_SIZES_H__
|
||||
|
||||
#define SZ_32K 0x00008000
|
||||
|
||||
#endif
|
||||
14
third_party/bpftool/include/linux/stringify.h
vendored
Normal file
14
third_party/bpftool/include/linux/stringify.h
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_STRINGIFY_H
|
||||
#define __LINUX_STRINGIFY_H
|
||||
|
||||
/* Indirect stringification. Doing two levels allows the parameter to be a
|
||||
* macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
|
||||
* converts to "bar".
|
||||
*/
|
||||
|
||||
#define __stringify_1(x...) #x
|
||||
#define __stringify(x...) __stringify_1(x)
|
||||
|
||||
#endif
|
||||
35
third_party/bpftool/include/linux/types.h
vendored
Normal file
35
third_party/bpftool/include/linux/types.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_TYPES_H
|
||||
#define __LINUX_TYPES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/posix_types.h>
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef __u32 u32;
|
||||
typedef __u8 u8;
|
||||
|
||||
#define __bitwise__
|
||||
#define __bitwise __bitwise__
|
||||
|
||||
typedef __u16 __bitwise __le16;
|
||||
typedef __u16 __bitwise __be16;
|
||||
typedef __u32 __bitwise __le32;
|
||||
typedef __u32 __bitwise __be32;
|
||||
typedef __u64 __bitwise __le64;
|
||||
typedef __u64 __bitwise __be64;
|
||||
|
||||
#ifndef __aligned_u64
|
||||
# define __aligned_u64 __u64 __attribute__((aligned(8)))
|
||||
#endif
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#endif
|
||||
55
third_party/bpftool/include/tools/dis-asm-compat.h
vendored
Normal file
55
third_party/bpftool/include/tools/dis-asm-compat.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
|
||||
#ifndef _TOOLS_DIS_ASM_COMPAT_H
|
||||
#define _TOOLS_DIS_ASM_COMPAT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dis-asm.h>
|
||||
|
||||
/* define types for older binutils version, to centralize ifdef'ery a bit */
|
||||
#ifndef DISASM_INIT_STYLED
|
||||
enum disassembler_style {DISASSEMBLER_STYLE_NOT_EMPTY};
|
||||
typedef int (*fprintf_styled_ftype) (void *, enum disassembler_style, const char*, ...);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Trivial fprintf wrapper to be used as the fprintf_styled_func argument to
|
||||
* init_disassemble_info_compat() when normal fprintf suffices.
|
||||
*/
|
||||
static inline int fprintf_styled(void *out,
|
||||
enum disassembler_style style,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int r;
|
||||
|
||||
(void)style;
|
||||
|
||||
va_start(args, fmt);
|
||||
r = vfprintf(out, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper for init_disassemble_info() that hides version
|
||||
* differences. Depending on binutils version and architecture either
|
||||
* fprintf_func or fprintf_styled_func will be called.
|
||||
*/
|
||||
static inline void init_disassemble_info_compat(struct disassemble_info *info,
|
||||
void *stream,
|
||||
fprintf_ftype unstyled_func,
|
||||
fprintf_styled_ftype styled_func)
|
||||
{
|
||||
#ifdef DISASM_INIT_STYLED
|
||||
init_disassemble_info(info, stream,
|
||||
unstyled_func,
|
||||
styled_func);
|
||||
#else
|
||||
(void)styled_func;
|
||||
init_disassemble_info(info, stream,
|
||||
unstyled_func);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _TOOLS_DIS_ASM_COMPAT_H */
|
||||
15
third_party/bpftool/include/uapi/asm-generic/bitsperlong.h
vendored
Normal file
15
third_party/bpftool/include/uapi/asm-generic/bitsperlong.h
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _UAPI__ASM_GENERIC_BITS_PER_LONG
|
||||
#define _UAPI__ASM_GENERIC_BITS_PER_LONG
|
||||
|
||||
/*
|
||||
* There seems to be no way of detecting this automatically from user
|
||||
* space, so 64 bit architectures should override this in their
|
||||
* bitsperlong.h. In particular, an architecture that supports
|
||||
* both 32 and 64 bit user space must not rely on CONFIG_64BIT
|
||||
* to decide it, but rather check a compiler provided macro.
|
||||
*/
|
||||
#ifndef __BITS_PER_LONG
|
||||
#define __BITS_PER_LONG 32
|
||||
#endif
|
||||
|
||||
#endif /* _UAPI__ASM_GENERIC_BITS_PER_LONG */
|
||||
7198
third_party/bpftool/include/uapi/linux/bpf.h
vendored
Normal file
7198
third_party/bpftool/include/uapi/linux/bpf.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
57
third_party/bpftool/include/uapi/linux/bpf_common.h
vendored
Normal file
57
third_party/bpftool/include/uapi/linux/bpf_common.h
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef _UAPI__LINUX_BPF_COMMON_H__
|
||||
#define _UAPI__LINUX_BPF_COMMON_H__
|
||||
|
||||
/* Instruction classes */
|
||||
#define BPF_CLASS(code) ((code) & 0x07)
|
||||
#define BPF_LD 0x00
|
||||
#define BPF_LDX 0x01
|
||||
#define BPF_ST 0x02
|
||||
#define BPF_STX 0x03
|
||||
#define BPF_ALU 0x04
|
||||
#define BPF_JMP 0x05
|
||||
#define BPF_RET 0x06
|
||||
#define BPF_MISC 0x07
|
||||
|
||||
/* ld/ldx fields */
|
||||
#define BPF_SIZE(code) ((code) & 0x18)
|
||||
#define BPF_W 0x00 /* 32-bit */
|
||||
#define BPF_H 0x08 /* 16-bit */
|
||||
#define BPF_B 0x10 /* 8-bit */
|
||||
/* eBPF BPF_DW 0x18 64-bit */
|
||||
#define BPF_MODE(code) ((code) & 0xe0)
|
||||
#define BPF_IMM 0x00
|
||||
#define BPF_ABS 0x20
|
||||
#define BPF_IND 0x40
|
||||
#define BPF_MEM 0x60
|
||||
#define BPF_LEN 0x80
|
||||
#define BPF_MSH 0xa0
|
||||
|
||||
/* alu/jmp fields */
|
||||
#define BPF_OP(code) ((code) & 0xf0)
|
||||
#define BPF_ADD 0x00
|
||||
#define BPF_SUB 0x10
|
||||
#define BPF_MUL 0x20
|
||||
#define BPF_DIV 0x30
|
||||
#define BPF_OR 0x40
|
||||
#define BPF_AND 0x50
|
||||
#define BPF_LSH 0x60
|
||||
#define BPF_RSH 0x70
|
||||
#define BPF_NEG 0x80
|
||||
#define BPF_MOD 0x90
|
||||
#define BPF_XOR 0xa0
|
||||
|
||||
#define BPF_JA 0x00
|
||||
#define BPF_JEQ 0x10
|
||||
#define BPF_JGT 0x20
|
||||
#define BPF_JGE 0x30
|
||||
#define BPF_JSET 0x40
|
||||
#define BPF_SRC(code) ((code) & 0x08)
|
||||
#define BPF_K 0x00
|
||||
#define BPF_X 0x08
|
||||
|
||||
#ifndef BPF_MAXINSNS
|
||||
#define BPF_MAXINSNS 4096
|
||||
#endif
|
||||
|
||||
#endif /* _UAPI__LINUX_BPF_COMMON_H__ */
|
||||
200
third_party/bpftool/include/uapi/linux/btf.h
vendored
Normal file
200
third_party/bpftool/include/uapi/linux/btf.h
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#ifndef _UAPI__LINUX_BTF_H__
|
||||
#define _UAPI__LINUX_BTF_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTF_MAGIC 0xeB9F
|
||||
#define BTF_VERSION 1
|
||||
|
||||
struct btf_header {
|
||||
__u16 magic;
|
||||
__u8 version;
|
||||
__u8 flags;
|
||||
__u32 hdr_len;
|
||||
|
||||
/* All offsets are in bytes relative to the end of this header */
|
||||
__u32 type_off; /* offset of type section */
|
||||
__u32 type_len; /* length of type section */
|
||||
__u32 str_off; /* offset of string section */
|
||||
__u32 str_len; /* length of string section */
|
||||
};
|
||||
|
||||
/* Max # of type identifier */
|
||||
#define BTF_MAX_TYPE 0x000fffff
|
||||
/* Max offset into the string section */
|
||||
#define BTF_MAX_NAME_OFFSET 0x00ffffff
|
||||
/* Max # of struct/union/enum members or func args */
|
||||
#define BTF_MAX_VLEN 0xffff
|
||||
|
||||
struct btf_type {
|
||||
__u32 name_off;
|
||||
/* "info" bits arrangement
|
||||
* bits 0-15: vlen (e.g. # of struct's members)
|
||||
* 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, enum, fwd and enum64
|
||||
*/
|
||||
__u32 info;
|
||||
/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
|
||||
* FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
__u32 size;
|
||||
__u32 type;
|
||||
};
|
||||
};
|
||||
|
||||
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x1f)
|
||||
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
||||
#define BTF_INFO_KFLAG(info) ((info) >> 31)
|
||||
|
||||
enum {
|
||||
BTF_KIND_UNKN = 0, /* Unknown */
|
||||
BTF_KIND_INT = 1, /* Integer */
|
||||
BTF_KIND_PTR = 2, /* Pointer */
|
||||
BTF_KIND_ARRAY = 3, /* Array */
|
||||
BTF_KIND_STRUCT = 4, /* Struct */
|
||||
BTF_KIND_UNION = 5, /* Union */
|
||||
BTF_KIND_ENUM = 6, /* Enumeration up to 32-bit values */
|
||||
BTF_KIND_FWD = 7, /* Forward */
|
||||
BTF_KIND_TYPEDEF = 8, /* Typedef */
|
||||
BTF_KIND_VOLATILE = 9, /* Volatile */
|
||||
BTF_KIND_CONST = 10, /* Const */
|
||||
BTF_KIND_RESTRICT = 11, /* Restrict */
|
||||
BTF_KIND_FUNC = 12, /* Function */
|
||||
BTF_KIND_FUNC_PROTO = 13, /* Function Proto */
|
||||
BTF_KIND_VAR = 14, /* Variable */
|
||||
BTF_KIND_DATASEC = 15, /* Section */
|
||||
BTF_KIND_FLOAT = 16, /* Floating point */
|
||||
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
|
||||
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
|
||||
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
|
||||
|
||||
NR_BTF_KINDS,
|
||||
BTF_KIND_MAX = NR_BTF_KINDS - 1,
|
||||
};
|
||||
|
||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||
* followed by extra data.
|
||||
*/
|
||||
|
||||
/* BTF_KIND_INT is followed by a u32 and the following
|
||||
* is the 32 bits arrangement:
|
||||
*/
|
||||
#define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24)
|
||||
#define BTF_INT_OFFSET(VAL) (((VAL) & 0x00ff0000) >> 16)
|
||||
#define BTF_INT_BITS(VAL) ((VAL) & 0x000000ff)
|
||||
|
||||
/* Attributes stored in the BTF_INT_ENCODING */
|
||||
#define BTF_INT_SIGNED (1 << 0)
|
||||
#define BTF_INT_CHAR (1 << 1)
|
||||
#define BTF_INT_BOOL (1 << 2)
|
||||
|
||||
/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
|
||||
* The exact number of btf_enum is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_enum {
|
||||
__u32 name_off;
|
||||
__s32 val;
|
||||
};
|
||||
|
||||
/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
|
||||
struct btf_array {
|
||||
__u32 type;
|
||||
__u32 index_type;
|
||||
__u32 nelems;
|
||||
};
|
||||
|
||||
/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
|
||||
* by multiple "struct btf_member". The exact number
|
||||
* of btf_member is stored in the vlen (of the info in
|
||||
* "struct btf_type").
|
||||
*/
|
||||
struct btf_member {
|
||||
__u32 name_off;
|
||||
__u32 type;
|
||||
/* If the type info kind_flag is set, the btf_member offset
|
||||
* contains both member bitfield size and bit offset. The
|
||||
* bitfield size is set for bitfield members. If the type
|
||||
* info kind_flag is not set, the offset contains only bit
|
||||
* offset.
|
||||
*/
|
||||
__u32 offset;
|
||||
};
|
||||
|
||||
/* If the struct/union type info kind_flag is set, the
|
||||
* following two macros are used to access bitfield_size
|
||||
* and bit_offset from btf_member.offset.
|
||||
*/
|
||||
#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
|
||||
#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff)
|
||||
|
||||
/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
|
||||
* The exact number of btf_param is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_param {
|
||||
__u32 name_off;
|
||||
__u32 type;
|
||||
};
|
||||
|
||||
enum {
|
||||
BTF_VAR_STATIC = 0,
|
||||
BTF_VAR_GLOBAL_ALLOCATED = 1,
|
||||
BTF_VAR_GLOBAL_EXTERN = 2,
|
||||
};
|
||||
|
||||
enum btf_func_linkage {
|
||||
BTF_FUNC_STATIC = 0,
|
||||
BTF_FUNC_GLOBAL = 1,
|
||||
BTF_FUNC_EXTERN = 2,
|
||||
};
|
||||
|
||||
/* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
|
||||
* additional information related to the variable such as its linkage.
|
||||
*/
|
||||
struct btf_var {
|
||||
__u32 linkage;
|
||||
};
|
||||
|
||||
/* BTF_KIND_DATASEC is followed by multiple "struct btf_var_secinfo"
|
||||
* to describe all BTF_KIND_VAR types it contains along with it's
|
||||
* in-section offset as well as size.
|
||||
*/
|
||||
struct btf_var_secinfo {
|
||||
__u32 type;
|
||||
__u32 offset;
|
||||
__u32 size;
|
||||
};
|
||||
|
||||
/* BTF_KIND_DECL_TAG is followed by a single "struct btf_decl_tag" to describe
|
||||
* additional information related to the tag applied location.
|
||||
* If component_idx == -1, the tag is applied to a struct, union,
|
||||
* variable or function. Otherwise, it is applied to a struct/union
|
||||
* member or a func argument, and component_idx indicates which member
|
||||
* or argument (0 ... vlen-1).
|
||||
*/
|
||||
struct btf_decl_tag {
|
||||
__s32 component_idx;
|
||||
};
|
||||
|
||||
/* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64".
|
||||
* The exact number of btf_enum64 is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_enum64 {
|
||||
__u32 name_off;
|
||||
__u32 val_lo32;
|
||||
__u32 val_hi32;
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_BTF_H__ */
|
||||
36
third_party/bpftool/include/uapi/linux/const.h
vendored
Normal file
36
third_party/bpftool/include/uapi/linux/const.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* const.h: Macros for dealing with constants. */
|
||||
|
||||
#ifndef _UAPI_LINUX_CONST_H
|
||||
#define _UAPI_LINUX_CONST_H
|
||||
|
||||
/* Some constant macros are used in both assembler and
|
||||
* C code. Therefore we cannot annotate them always with
|
||||
* 'UL' and other type specifiers unilaterally. We
|
||||
* use the following macros to deal with this.
|
||||
*
|
||||
* Similarly, _AT() will cast an expression with a type in C, but
|
||||
* leave it unchanged in asm.
|
||||
*/
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
#define _AC(X,Y) X
|
||||
#define _AT(T,X) X
|
||||
#else
|
||||
#define __AC(X,Y) (X##Y)
|
||||
#define _AC(X,Y) __AC(X,Y)
|
||||
#define _AT(T,X) ((T)(X))
|
||||
#endif
|
||||
|
||||
#define _UL(x) (_AC(x, UL))
|
||||
#define _ULL(x) (_AC(x, ULL))
|
||||
|
||||
#define _BITUL(x) (_UL(1) << (x))
|
||||
#define _BITULL(x) (_ULL(1) << (x))
|
||||
|
||||
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)
|
||||
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
|
||||
|
||||
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
|
||||
#endif /* _UAPI_LINUX_CONST_H */
|
||||
1284
third_party/bpftool/include/uapi/linux/if_link.h
vendored
Normal file
1284
third_party/bpftool/include/uapi/linux/if_link.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
252
third_party/bpftool/include/uapi/linux/netlink.h
vendored
Normal file
252
third_party/bpftool/include/uapi/linux/netlink.h
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef _UAPI__LINUX_NETLINK_H
|
||||
#define _UAPI__LINUX_NETLINK_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/socket.h> /* for __kernel_sa_family_t */
|
||||
#include <linux/types.h>
|
||||
|
||||
#define NETLINK_ROUTE 0 /* Routing/device hook */
|
||||
#define NETLINK_UNUSED 1 /* Unused number */
|
||||
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
|
||||
#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
|
||||
#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
|
||||
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
|
||||
#define NETLINK_XFRM 6 /* ipsec */
|
||||
#define NETLINK_SELINUX 7 /* SELinux event notifications */
|
||||
#define NETLINK_ISCSI 8 /* Open-iSCSI */
|
||||
#define NETLINK_AUDIT 9 /* auditing */
|
||||
#define NETLINK_FIB_LOOKUP 10
|
||||
#define NETLINK_CONNECTOR 11
|
||||
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
|
||||
#define NETLINK_IP6_FW 13
|
||||
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
|
||||
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
|
||||
#define NETLINK_GENERIC 16
|
||||
/* leave room for NETLINK_DM (DM Events) */
|
||||
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
|
||||
#define NETLINK_ECRYPTFS 19
|
||||
#define NETLINK_RDMA 20
|
||||
#define NETLINK_CRYPTO 21 /* Crypto layer */
|
||||
#define NETLINK_SMC 22 /* SMC monitoring */
|
||||
|
||||
#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
|
||||
|
||||
#define MAX_LINKS 32
|
||||
|
||||
struct sockaddr_nl {
|
||||
__kernel_sa_family_t nl_family; /* AF_NETLINK */
|
||||
unsigned short nl_pad; /* zero */
|
||||
__u32 nl_pid; /* port ID */
|
||||
__u32 nl_groups; /* multicast groups mask */
|
||||
};
|
||||
|
||||
struct nlmsghdr {
|
||||
__u32 nlmsg_len; /* Length of message including header */
|
||||
__u16 nlmsg_type; /* Message content */
|
||||
__u16 nlmsg_flags; /* Additional flags */
|
||||
__u32 nlmsg_seq; /* Sequence number */
|
||||
__u32 nlmsg_pid; /* Sending process port ID */
|
||||
};
|
||||
|
||||
/* Flags values */
|
||||
|
||||
#define NLM_F_REQUEST 0x01 /* It is request message. */
|
||||
#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */
|
||||
#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */
|
||||
#define NLM_F_ECHO 0x08 /* Echo this request */
|
||||
#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */
|
||||
#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */
|
||||
|
||||
/* Modifiers to GET request */
|
||||
#define NLM_F_ROOT 0x100 /* specify tree root */
|
||||
#define NLM_F_MATCH 0x200 /* return all matching */
|
||||
#define NLM_F_ATOMIC 0x400 /* atomic GET */
|
||||
#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
|
||||
|
||||
/* Modifiers to NEW request */
|
||||
#define NLM_F_REPLACE 0x100 /* Override existing */
|
||||
#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
|
||||
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
|
||||
#define NLM_F_APPEND 0x800 /* Add to end of list */
|
||||
|
||||
/* Modifiers to DELETE request */
|
||||
#define NLM_F_NONREC 0x100 /* Do not delete recursively */
|
||||
|
||||
/* Flags for ACK message */
|
||||
#define NLM_F_CAPPED 0x100 /* request was capped */
|
||||
#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
|
||||
|
||||
/*
|
||||
4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
|
||||
4.4BSD CHANGE NLM_F_REPLACE
|
||||
|
||||
True CHANGE NLM_F_CREATE|NLM_F_REPLACE
|
||||
Append NLM_F_CREATE
|
||||
Check NLM_F_EXCL
|
||||
*/
|
||||
|
||||
#define NLMSG_ALIGNTO 4U
|
||||
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
|
||||
#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
|
||||
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
|
||||
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
|
||||
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
|
||||
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
|
||||
(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
|
||||
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
|
||||
(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
|
||||
(nlh)->nlmsg_len <= (len))
|
||||
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
|
||||
|
||||
#define NLMSG_NOOP 0x1 /* Nothing. */
|
||||
#define NLMSG_ERROR 0x2 /* Error */
|
||||
#define NLMSG_DONE 0x3 /* End of a dump */
|
||||
#define NLMSG_OVERRUN 0x4 /* Data lost */
|
||||
|
||||
#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */
|
||||
|
||||
struct nlmsgerr {
|
||||
int error;
|
||||
struct nlmsghdr msg;
|
||||
/*
|
||||
* followed by the message contents unless NETLINK_CAP_ACK was set
|
||||
* or the ACK indicates success (error == 0)
|
||||
* message length is aligned with NLMSG_ALIGN()
|
||||
*/
|
||||
/*
|
||||
* followed by TLVs defined in enum nlmsgerr_attrs
|
||||
* if NETLINK_EXT_ACK was set
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nlmsgerr_attrs - nlmsgerr attributes
|
||||
* @NLMSGERR_ATTR_UNUSED: unused
|
||||
* @NLMSGERR_ATTR_MSG: error message string (string)
|
||||
* @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
|
||||
* message, counting from the beginning of the header (u32)
|
||||
* @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
|
||||
* be used - in the success case - to identify a created
|
||||
* object or operation or similar (binary)
|
||||
* @__NLMSGERR_ATTR_MAX: number of attributes
|
||||
* @NLMSGERR_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nlmsgerr_attrs {
|
||||
NLMSGERR_ATTR_UNUSED,
|
||||
NLMSGERR_ATTR_MSG,
|
||||
NLMSGERR_ATTR_OFFS,
|
||||
NLMSGERR_ATTR_COOKIE,
|
||||
|
||||
__NLMSGERR_ATTR_MAX,
|
||||
NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
|
||||
};
|
||||
|
||||
#define NETLINK_ADD_MEMBERSHIP 1
|
||||
#define NETLINK_DROP_MEMBERSHIP 2
|
||||
#define NETLINK_PKTINFO 3
|
||||
#define NETLINK_BROADCAST_ERROR 4
|
||||
#define NETLINK_NO_ENOBUFS 5
|
||||
#ifndef __KERNEL__
|
||||
#define NETLINK_RX_RING 6
|
||||
#define NETLINK_TX_RING 7
|
||||
#endif
|
||||
#define NETLINK_LISTEN_ALL_NSID 8
|
||||
#define NETLINK_LIST_MEMBERSHIPS 9
|
||||
#define NETLINK_CAP_ACK 10
|
||||
#define NETLINK_EXT_ACK 11
|
||||
#define NETLINK_GET_STRICT_CHK 12
|
||||
|
||||
struct nl_pktinfo {
|
||||
__u32 group;
|
||||
};
|
||||
|
||||
struct nl_mmap_req {
|
||||
unsigned int nm_block_size;
|
||||
unsigned int nm_block_nr;
|
||||
unsigned int nm_frame_size;
|
||||
unsigned int nm_frame_nr;
|
||||
};
|
||||
|
||||
struct nl_mmap_hdr {
|
||||
unsigned int nm_status;
|
||||
unsigned int nm_len;
|
||||
__u32 nm_group;
|
||||
/* credentials */
|
||||
__u32 nm_pid;
|
||||
__u32 nm_uid;
|
||||
__u32 nm_gid;
|
||||
};
|
||||
|
||||
#ifndef __KERNEL__
|
||||
enum nl_mmap_status {
|
||||
NL_MMAP_STATUS_UNUSED,
|
||||
NL_MMAP_STATUS_RESERVED,
|
||||
NL_MMAP_STATUS_VALID,
|
||||
NL_MMAP_STATUS_COPY,
|
||||
NL_MMAP_STATUS_SKIP,
|
||||
};
|
||||
|
||||
#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
|
||||
#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
|
||||
#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
|
||||
#endif
|
||||
|
||||
#define NET_MAJOR 36 /* Major 36 is reserved for networking */
|
||||
|
||||
enum {
|
||||
NETLINK_UNCONNECTED = 0,
|
||||
NETLINK_CONNECTED,
|
||||
};
|
||||
|
||||
/*
|
||||
* <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
|
||||
* +---------------------+- - -+- - - - - - - - - -+- - -+
|
||||
* | Header | Pad | Payload | Pad |
|
||||
* | (struct nlattr) | ing | | ing |
|
||||
* +---------------------+- - -+- - - - - - - - - -+- - -+
|
||||
* <-------------- nlattr->nla_len -------------->
|
||||
*/
|
||||
|
||||
struct nlattr {
|
||||
__u16 nla_len;
|
||||
__u16 nla_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* nla_type (16 bits)
|
||||
* +---+---+-------------------------------+
|
||||
* | N | O | Attribute Type |
|
||||
* +---+---+-------------------------------+
|
||||
* N := Carries nested attributes
|
||||
* O := Payload stored in network byte order
|
||||
*
|
||||
* Note: The N and O flag are mutually exclusive.
|
||||
*/
|
||||
#define NLA_F_NESTED (1 << 15)
|
||||
#define NLA_F_NET_BYTEORDER (1 << 14)
|
||||
#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
|
||||
|
||||
#define NLA_ALIGNTO 4
|
||||
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
|
||||
#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
|
||||
|
||||
/* Generic 32 bitflags attribute content sent to the kernel.
|
||||
*
|
||||
* The value is a bitmap that defines the values being set
|
||||
* The selector is a bitmask that defines which value is legit
|
||||
*
|
||||
* Examples:
|
||||
* value = 0x0, and selector = 0x1
|
||||
* implies we are selecting bit 1 and we want to set its value to 0.
|
||||
*
|
||||
* value = 0x2, and selector = 0x2
|
||||
* implies we are selecting bit 2 and we want to set its value to 1.
|
||||
*
|
||||
*/
|
||||
struct nla_bitfield32 {
|
||||
__u32 value;
|
||||
__u32 selector;
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_NETLINK_H */
|
||||
1449
third_party/bpftool/include/uapi/linux/perf_event.h
vendored
Normal file
1449
third_party/bpftool/include/uapi/linux/perf_event.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
612
third_party/bpftool/include/uapi/linux/pkt_cls.h
vendored
Normal file
612
third_party/bpftool/include/uapi/linux/pkt_cls.h
vendored
Normal file
@@ -0,0 +1,612 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef __LINUX_PKT_CLS_H
|
||||
#define __LINUX_PKT_CLS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
|
||||
#define TC_COOKIE_MAX_SIZE 16
|
||||
|
||||
/* Action attributes */
|
||||
enum {
|
||||
TCA_ACT_UNSPEC,
|
||||
TCA_ACT_KIND,
|
||||
TCA_ACT_OPTIONS,
|
||||
TCA_ACT_INDEX,
|
||||
TCA_ACT_STATS,
|
||||
TCA_ACT_PAD,
|
||||
TCA_ACT_COOKIE,
|
||||
__TCA_ACT_MAX
|
||||
};
|
||||
|
||||
#define TCA_ACT_MAX __TCA_ACT_MAX
|
||||
#define TCA_OLD_COMPAT (TCA_ACT_MAX+1)
|
||||
#define TCA_ACT_MAX_PRIO 32
|
||||
#define TCA_ACT_BIND 1
|
||||
#define TCA_ACT_NOBIND 0
|
||||
#define TCA_ACT_UNBIND 1
|
||||
#define TCA_ACT_NOUNBIND 0
|
||||
#define TCA_ACT_REPLACE 1
|
||||
#define TCA_ACT_NOREPLACE 0
|
||||
|
||||
#define TC_ACT_UNSPEC (-1)
|
||||
#define TC_ACT_OK 0
|
||||
#define TC_ACT_RECLASSIFY 1
|
||||
#define TC_ACT_SHOT 2
|
||||
#define TC_ACT_PIPE 3
|
||||
#define TC_ACT_STOLEN 4
|
||||
#define TC_ACT_QUEUED 5
|
||||
#define TC_ACT_REPEAT 6
|
||||
#define TC_ACT_REDIRECT 7
|
||||
#define TC_ACT_TRAP 8 /* For hw path, this means "trap to cpu"
|
||||
* and don't further process the frame
|
||||
* in hardware. For sw path, this is
|
||||
* equivalent of TC_ACT_STOLEN - drop
|
||||
* the skb and act like everything
|
||||
* is alright.
|
||||
*/
|
||||
#define TC_ACT_VALUE_MAX TC_ACT_TRAP
|
||||
|
||||
/* There is a special kind of actions called "extended actions",
|
||||
* which need a value parameter. These have a local opcode located in
|
||||
* the highest nibble, starting from 1. The rest of the bits
|
||||
* are used to carry the value. These two parts together make
|
||||
* a combined opcode.
|
||||
*/
|
||||
#define __TC_ACT_EXT_SHIFT 28
|
||||
#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT)
|
||||
#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1)
|
||||
#define TC_ACT_EXT_OPCODE(combined) ((combined) & (~TC_ACT_EXT_VAL_MASK))
|
||||
#define TC_ACT_EXT_CMP(combined, opcode) (TC_ACT_EXT_OPCODE(combined) == opcode)
|
||||
|
||||
#define TC_ACT_JUMP __TC_ACT_EXT(1)
|
||||
#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2)
|
||||
#define TC_ACT_EXT_OPCODE_MAX TC_ACT_GOTO_CHAIN
|
||||
|
||||
/* Action type identifiers*/
|
||||
enum {
|
||||
TCA_ID_UNSPEC=0,
|
||||
TCA_ID_POLICE=1,
|
||||
/* other actions go here */
|
||||
__TCA_ID_MAX=255
|
||||
};
|
||||
|
||||
#define TCA_ID_MAX __TCA_ID_MAX
|
||||
|
||||
struct tc_police {
|
||||
__u32 index;
|
||||
int action;
|
||||
#define TC_POLICE_UNSPEC TC_ACT_UNSPEC
|
||||
#define TC_POLICE_OK TC_ACT_OK
|
||||
#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY
|
||||
#define TC_POLICE_SHOT TC_ACT_SHOT
|
||||
#define TC_POLICE_PIPE TC_ACT_PIPE
|
||||
|
||||
__u32 limit;
|
||||
__u32 burst;
|
||||
__u32 mtu;
|
||||
struct tc_ratespec rate;
|
||||
struct tc_ratespec peakrate;
|
||||
int refcnt;
|
||||
int bindcnt;
|
||||
__u32 capab;
|
||||
};
|
||||
|
||||
struct tcf_t {
|
||||
__u64 install;
|
||||
__u64 lastuse;
|
||||
__u64 expires;
|
||||
__u64 firstuse;
|
||||
};
|
||||
|
||||
struct tc_cnt {
|
||||
int refcnt;
|
||||
int bindcnt;
|
||||
};
|
||||
|
||||
#define tc_gen \
|
||||
__u32 index; \
|
||||
__u32 capab; \
|
||||
int action; \
|
||||
int refcnt; \
|
||||
int bindcnt
|
||||
|
||||
enum {
|
||||
TCA_POLICE_UNSPEC,
|
||||
TCA_POLICE_TBF,
|
||||
TCA_POLICE_RATE,
|
||||
TCA_POLICE_PEAKRATE,
|
||||
TCA_POLICE_AVRATE,
|
||||
TCA_POLICE_RESULT,
|
||||
TCA_POLICE_TM,
|
||||
TCA_POLICE_PAD,
|
||||
__TCA_POLICE_MAX
|
||||
#define TCA_POLICE_RESULT TCA_POLICE_RESULT
|
||||
};
|
||||
|
||||
#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1)
|
||||
|
||||
/* tca flags definitions */
|
||||
#define TCA_CLS_FLAGS_SKIP_HW (1 << 0) /* don't offload filter to HW */
|
||||
#define TCA_CLS_FLAGS_SKIP_SW (1 << 1) /* don't use filter in SW */
|
||||
#define TCA_CLS_FLAGS_IN_HW (1 << 2) /* filter is offloaded to HW */
|
||||
#define TCA_CLS_FLAGS_NOT_IN_HW (1 << 3) /* filter isn't offloaded to HW */
|
||||
#define TCA_CLS_FLAGS_VERBOSE (1 << 4) /* verbose logging */
|
||||
|
||||
/* U32 filters */
|
||||
|
||||
#define TC_U32_HTID(h) ((h)&0xFFF00000)
|
||||
#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
|
||||
#define TC_U32_HASH(h) (((h)>>12)&0xFF)
|
||||
#define TC_U32_NODE(h) ((h)&0xFFF)
|
||||
#define TC_U32_KEY(h) ((h)&0xFFFFF)
|
||||
#define TC_U32_UNSPEC 0
|
||||
#define TC_U32_ROOT (0xFFF00000)
|
||||
|
||||
enum {
|
||||
TCA_U32_UNSPEC,
|
||||
TCA_U32_CLASSID,
|
||||
TCA_U32_HASH,
|
||||
TCA_U32_LINK,
|
||||
TCA_U32_DIVISOR,
|
||||
TCA_U32_SEL,
|
||||
TCA_U32_POLICE,
|
||||
TCA_U32_ACT,
|
||||
TCA_U32_INDEV,
|
||||
TCA_U32_PCNT,
|
||||
TCA_U32_MARK,
|
||||
TCA_U32_FLAGS,
|
||||
TCA_U32_PAD,
|
||||
__TCA_U32_MAX
|
||||
};
|
||||
|
||||
#define TCA_U32_MAX (__TCA_U32_MAX - 1)
|
||||
|
||||
struct tc_u32_key {
|
||||
__be32 mask;
|
||||
__be32 val;
|
||||
int off;
|
||||
int offmask;
|
||||
};
|
||||
|
||||
struct tc_u32_sel {
|
||||
unsigned char flags;
|
||||
unsigned char offshift;
|
||||
unsigned char nkeys;
|
||||
|
||||
__be16 offmask;
|
||||
__u16 off;
|
||||
short offoff;
|
||||
|
||||
short hoff;
|
||||
__be32 hmask;
|
||||
struct tc_u32_key keys[];
|
||||
};
|
||||
|
||||
struct tc_u32_mark {
|
||||
__u32 val;
|
||||
__u32 mask;
|
||||
__u32 success;
|
||||
};
|
||||
|
||||
struct tc_u32_pcnt {
|
||||
__u64 rcnt;
|
||||
__u64 rhit;
|
||||
__u64 kcnts[];
|
||||
};
|
||||
|
||||
/* Flags */
|
||||
|
||||
#define TC_U32_TERMINAL 1
|
||||
#define TC_U32_OFFSET 2
|
||||
#define TC_U32_VAROFFSET 4
|
||||
#define TC_U32_EAT 8
|
||||
|
||||
#define TC_U32_MAXDEPTH 8
|
||||
|
||||
|
||||
/* RSVP filter */
|
||||
|
||||
enum {
|
||||
TCA_RSVP_UNSPEC,
|
||||
TCA_RSVP_CLASSID,
|
||||
TCA_RSVP_DST,
|
||||
TCA_RSVP_SRC,
|
||||
TCA_RSVP_PINFO,
|
||||
TCA_RSVP_POLICE,
|
||||
TCA_RSVP_ACT,
|
||||
__TCA_RSVP_MAX
|
||||
};
|
||||
|
||||
#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 )
|
||||
|
||||
struct tc_rsvp_gpi {
|
||||
__u32 key;
|
||||
__u32 mask;
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct tc_rsvp_pinfo {
|
||||
struct tc_rsvp_gpi dpi;
|
||||
struct tc_rsvp_gpi spi;
|
||||
__u8 protocol;
|
||||
__u8 tunnelid;
|
||||
__u8 tunnelhdr;
|
||||
__u8 pad;
|
||||
};
|
||||
|
||||
/* ROUTE filter */
|
||||
|
||||
enum {
|
||||
TCA_ROUTE4_UNSPEC,
|
||||
TCA_ROUTE4_CLASSID,
|
||||
TCA_ROUTE4_TO,
|
||||
TCA_ROUTE4_FROM,
|
||||
TCA_ROUTE4_IIF,
|
||||
TCA_ROUTE4_POLICE,
|
||||
TCA_ROUTE4_ACT,
|
||||
__TCA_ROUTE4_MAX
|
||||
};
|
||||
|
||||
#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1)
|
||||
|
||||
|
||||
/* FW filter */
|
||||
|
||||
enum {
|
||||
TCA_FW_UNSPEC,
|
||||
TCA_FW_CLASSID,
|
||||
TCA_FW_POLICE,
|
||||
TCA_FW_INDEV,
|
||||
TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */
|
||||
TCA_FW_MASK,
|
||||
__TCA_FW_MAX
|
||||
};
|
||||
|
||||
#define TCA_FW_MAX (__TCA_FW_MAX - 1)
|
||||
|
||||
/* TC index filter */
|
||||
|
||||
enum {
|
||||
TCA_TCINDEX_UNSPEC,
|
||||
TCA_TCINDEX_HASH,
|
||||
TCA_TCINDEX_MASK,
|
||||
TCA_TCINDEX_SHIFT,
|
||||
TCA_TCINDEX_FALL_THROUGH,
|
||||
TCA_TCINDEX_CLASSID,
|
||||
TCA_TCINDEX_POLICE,
|
||||
TCA_TCINDEX_ACT,
|
||||
__TCA_TCINDEX_MAX
|
||||
};
|
||||
|
||||
#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1)
|
||||
|
||||
/* Flow filter */
|
||||
|
||||
enum {
|
||||
FLOW_KEY_SRC,
|
||||
FLOW_KEY_DST,
|
||||
FLOW_KEY_PROTO,
|
||||
FLOW_KEY_PROTO_SRC,
|
||||
FLOW_KEY_PROTO_DST,
|
||||
FLOW_KEY_IIF,
|
||||
FLOW_KEY_PRIORITY,
|
||||
FLOW_KEY_MARK,
|
||||
FLOW_KEY_NFCT,
|
||||
FLOW_KEY_NFCT_SRC,
|
||||
FLOW_KEY_NFCT_DST,
|
||||
FLOW_KEY_NFCT_PROTO_SRC,
|
||||
FLOW_KEY_NFCT_PROTO_DST,
|
||||
FLOW_KEY_RTCLASSID,
|
||||
FLOW_KEY_SKUID,
|
||||
FLOW_KEY_SKGID,
|
||||
FLOW_KEY_VLAN_TAG,
|
||||
FLOW_KEY_RXHASH,
|
||||
__FLOW_KEY_MAX,
|
||||
};
|
||||
|
||||
#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1)
|
||||
|
||||
enum {
|
||||
FLOW_MODE_MAP,
|
||||
FLOW_MODE_HASH,
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_FLOW_UNSPEC,
|
||||
TCA_FLOW_KEYS,
|
||||
TCA_FLOW_MODE,
|
||||
TCA_FLOW_BASECLASS,
|
||||
TCA_FLOW_RSHIFT,
|
||||
TCA_FLOW_ADDEND,
|
||||
TCA_FLOW_MASK,
|
||||
TCA_FLOW_XOR,
|
||||
TCA_FLOW_DIVISOR,
|
||||
TCA_FLOW_ACT,
|
||||
TCA_FLOW_POLICE,
|
||||
TCA_FLOW_EMATCHES,
|
||||
TCA_FLOW_PERTURB,
|
||||
__TCA_FLOW_MAX
|
||||
};
|
||||
|
||||
#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1)
|
||||
|
||||
/* Basic filter */
|
||||
|
||||
enum {
|
||||
TCA_BASIC_UNSPEC,
|
||||
TCA_BASIC_CLASSID,
|
||||
TCA_BASIC_EMATCHES,
|
||||
TCA_BASIC_ACT,
|
||||
TCA_BASIC_POLICE,
|
||||
__TCA_BASIC_MAX
|
||||
};
|
||||
|
||||
#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
|
||||
|
||||
|
||||
/* Cgroup classifier */
|
||||
|
||||
enum {
|
||||
TCA_CGROUP_UNSPEC,
|
||||
TCA_CGROUP_ACT,
|
||||
TCA_CGROUP_POLICE,
|
||||
TCA_CGROUP_EMATCHES,
|
||||
__TCA_CGROUP_MAX,
|
||||
};
|
||||
|
||||
#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
|
||||
|
||||
/* BPF classifier */
|
||||
|
||||
#define TCA_BPF_FLAG_ACT_DIRECT (1 << 0)
|
||||
|
||||
enum {
|
||||
TCA_BPF_UNSPEC,
|
||||
TCA_BPF_ACT,
|
||||
TCA_BPF_POLICE,
|
||||
TCA_BPF_CLASSID,
|
||||
TCA_BPF_OPS_LEN,
|
||||
TCA_BPF_OPS,
|
||||
TCA_BPF_FD,
|
||||
TCA_BPF_NAME,
|
||||
TCA_BPF_FLAGS,
|
||||
TCA_BPF_FLAGS_GEN,
|
||||
TCA_BPF_TAG,
|
||||
TCA_BPF_ID,
|
||||
__TCA_BPF_MAX,
|
||||
};
|
||||
|
||||
#define TCA_BPF_MAX (__TCA_BPF_MAX - 1)
|
||||
|
||||
/* Flower classifier */
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_UNSPEC,
|
||||
TCA_FLOWER_CLASSID,
|
||||
TCA_FLOWER_INDEV,
|
||||
TCA_FLOWER_ACT,
|
||||
TCA_FLOWER_KEY_ETH_DST, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_DST_MASK, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_SRC, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_SRC_MASK, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ETH_TYPE, /* be16 */
|
||||
TCA_FLOWER_KEY_IP_PROTO, /* u8 */
|
||||
TCA_FLOWER_KEY_IPV4_SRC, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV4_SRC_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV4_DST, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV4_DST_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_IPV6_SRC, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_IPV6_SRC_MASK, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_IPV6_DST, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_IPV6_DST_MASK, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_TCP_SRC, /* be16 */
|
||||
TCA_FLOWER_KEY_TCP_DST, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_SRC, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_DST, /* be16 */
|
||||
|
||||
TCA_FLOWER_FLAGS,
|
||||
TCA_FLOWER_KEY_VLAN_ID, /* be16 */
|
||||
TCA_FLOWER_KEY_VLAN_PRIO, /* u8 */
|
||||
TCA_FLOWER_KEY_VLAN_ETH_TYPE, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_KEY_ID, /* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_SRC, /* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,/* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_DST, /* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,/* be32 */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_SRC, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,/* struct in6_addr */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_DST, /* struct in6_addr */
|
||||
TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,/* struct in6_addr */
|
||||
|
||||
TCA_FLOWER_KEY_TCP_SRC_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_TCP_DST_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_SRC_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_UDP_DST_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_SCTP_SRC_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_SCTP_DST_MASK, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_SCTP_SRC, /* be16 */
|
||||
TCA_FLOWER_KEY_SCTP_DST, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, /* be16 */
|
||||
TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, /* be16 */
|
||||
TCA_FLOWER_KEY_ENC_UDP_DST_PORT, /* be16 */
|
||||
TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_FLAGS, /* be32 */
|
||||
TCA_FLOWER_KEY_FLAGS_MASK, /* be32 */
|
||||
|
||||
TCA_FLOWER_KEY_ICMPV4_CODE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV4_CODE_MASK,/* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV4_TYPE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,/* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_CODE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_CODE_MASK,/* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_TYPE, /* u8 */
|
||||
TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,/* u8 */
|
||||
|
||||
TCA_FLOWER_KEY_ARP_SIP, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_SIP_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_TIP, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_TIP_MASK, /* be32 */
|
||||
TCA_FLOWER_KEY_ARP_OP, /* u8 */
|
||||
TCA_FLOWER_KEY_ARP_OP_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_ARP_SHA, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ARP_SHA_MASK, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ARP_THA, /* ETH_ALEN */
|
||||
TCA_FLOWER_KEY_ARP_THA_MASK, /* ETH_ALEN */
|
||||
|
||||
TCA_FLOWER_KEY_MPLS_TTL, /* u8 - 8 bits */
|
||||
TCA_FLOWER_KEY_MPLS_BOS, /* u8 - 1 bit */
|
||||
TCA_FLOWER_KEY_MPLS_TC, /* u8 - 3 bits */
|
||||
TCA_FLOWER_KEY_MPLS_LABEL, /* be32 - 20 bits */
|
||||
|
||||
TCA_FLOWER_KEY_TCP_FLAGS, /* be16 */
|
||||
TCA_FLOWER_KEY_TCP_FLAGS_MASK, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_IP_TOS, /* u8 */
|
||||
TCA_FLOWER_KEY_IP_TOS_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_IP_TTL, /* u8 */
|
||||
TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */
|
||||
|
||||
TCA_FLOWER_KEY_CVLAN_ID, /* be16 */
|
||||
TCA_FLOWER_KEY_CVLAN_PRIO, /* u8 */
|
||||
TCA_FLOWER_KEY_CVLAN_ETH_TYPE, /* be16 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_IP_TOS, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */
|
||||
|
||||
TCA_FLOWER_KEY_ENC_OPTS,
|
||||
TCA_FLOWER_KEY_ENC_OPTS_MASK,
|
||||
|
||||
TCA_FLOWER_IN_HW_COUNT,
|
||||
|
||||
__TCA_FLOWER_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_MAX (__TCA_FLOWER_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_ENC_OPTS_UNSPEC,
|
||||
TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested
|
||||
* TCA_FLOWER_KEY_ENC_OPT_GENEVE_
|
||||
* attributes
|
||||
*/
|
||||
__TCA_FLOWER_KEY_ENC_OPTS_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC,
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, /* u16 */
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, /* 4 to 128 bytes */
|
||||
|
||||
__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \
|
||||
(__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0),
|
||||
TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
|
||||
};
|
||||
|
||||
/* Match-all classifier */
|
||||
|
||||
enum {
|
||||
TCA_MATCHALL_UNSPEC,
|
||||
TCA_MATCHALL_CLASSID,
|
||||
TCA_MATCHALL_ACT,
|
||||
TCA_MATCHALL_FLAGS,
|
||||
__TCA_MATCHALL_MAX,
|
||||
};
|
||||
|
||||
#define TCA_MATCHALL_MAX (__TCA_MATCHALL_MAX - 1)
|
||||
|
||||
/* Extended Matches */
|
||||
|
||||
struct tcf_ematch_tree_hdr {
|
||||
__u16 nmatches;
|
||||
__u16 progid;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_EMATCH_TREE_UNSPEC,
|
||||
TCA_EMATCH_TREE_HDR,
|
||||
TCA_EMATCH_TREE_LIST,
|
||||
__TCA_EMATCH_TREE_MAX
|
||||
};
|
||||
#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1)
|
||||
|
||||
struct tcf_ematch_hdr {
|
||||
__u16 matchid;
|
||||
__u16 kind;
|
||||
__u16 flags;
|
||||
__u16 pad; /* currently unused */
|
||||
};
|
||||
|
||||
/* 0 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
* +-----------------------+-+-+---+
|
||||
* | Unused |S|I| R |
|
||||
* +-----------------------+-+-+---+
|
||||
*
|
||||
* R(2) ::= relation to next ematch
|
||||
* where: 0 0 END (last ematch)
|
||||
* 0 1 AND
|
||||
* 1 0 OR
|
||||
* 1 1 Unused (invalid)
|
||||
* I(1) ::= invert result
|
||||
* S(1) ::= simple payload
|
||||
*/
|
||||
#define TCF_EM_REL_END 0
|
||||
#define TCF_EM_REL_AND (1<<0)
|
||||
#define TCF_EM_REL_OR (1<<1)
|
||||
#define TCF_EM_INVERT (1<<2)
|
||||
#define TCF_EM_SIMPLE (1<<3)
|
||||
|
||||
#define TCF_EM_REL_MASK 3
|
||||
#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK)
|
||||
|
||||
enum {
|
||||
TCF_LAYER_LINK,
|
||||
TCF_LAYER_NETWORK,
|
||||
TCF_LAYER_TRANSPORT,
|
||||
__TCF_LAYER_MAX
|
||||
};
|
||||
#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1)
|
||||
|
||||
/* Ematch type assignments
|
||||
* 1..32767 Reserved for ematches inside kernel tree
|
||||
* 32768..65535 Free to use, not reliable
|
||||
*/
|
||||
#define TCF_EM_CONTAINER 0
|
||||
#define TCF_EM_CMP 1
|
||||
#define TCF_EM_NBYTE 2
|
||||
#define TCF_EM_U32 3
|
||||
#define TCF_EM_META 4
|
||||
#define TCF_EM_TEXT 5
|
||||
#define TCF_EM_VLAN 6
|
||||
#define TCF_EM_CANID 7
|
||||
#define TCF_EM_IPSET 8
|
||||
#define TCF_EM_IPT 9
|
||||
#define TCF_EM_MAX 9
|
||||
|
||||
enum {
|
||||
TCF_EM_PROG_TC
|
||||
};
|
||||
|
||||
enum {
|
||||
TCF_EM_OPND_EQ,
|
||||
TCF_EM_OPND_GT,
|
||||
TCF_EM_OPND_LT
|
||||
};
|
||||
|
||||
#endif
|
||||
1164
third_party/bpftool/include/uapi/linux/pkt_sched.h
vendored
Normal file
1164
third_party/bpftool/include/uapi/linux/pkt_sched.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
30
third_party/bpftool/include/uapi/linux/tc_act/tc_bpf.h
vendored
Normal file
30
third_party/bpftool/include/uapi/linux/tc_act/tc_bpf.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_TC_BPF_H
|
||||
#define __LINUX_TC_BPF_H
|
||||
|
||||
#include <linux/pkt_cls.h>
|
||||
|
||||
struct tc_act_bpf {
|
||||
tc_gen;
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_ACT_BPF_UNSPEC,
|
||||
TCA_ACT_BPF_TM,
|
||||
TCA_ACT_BPF_PARMS,
|
||||
TCA_ACT_BPF_OPS_LEN,
|
||||
TCA_ACT_BPF_OPS,
|
||||
TCA_ACT_BPF_FD,
|
||||
TCA_ACT_BPF_NAME,
|
||||
TCA_ACT_BPF_PAD,
|
||||
TCA_ACT_BPF_TAG,
|
||||
TCA_ACT_BPF_ID,
|
||||
__TCA_ACT_BPF_MAX,
|
||||
};
|
||||
#define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1)
|
||||
|
||||
#endif
|
||||
1
third_party/bpftool/libbpf/.gitattributes
vendored
Normal file
1
third_party/bpftool/libbpf/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
assets/** export-ignore
|
||||
22
third_party/bpftool/libbpf/.readthedocs.yaml
vendored
Normal file
22
third_party/bpftool/libbpf/.readthedocs.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
builder: html
|
||||
configuration: docs/conf.py
|
||||
|
||||
formats:
|
||||
- htmlzip
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- requirements: docs/sphinx/requirements.txt
|
||||
1
third_party/bpftool/libbpf/BPF-CHECKPOINT-COMMIT
vendored
Normal file
1
third_party/bpftool/libbpf/BPF-CHECKPOINT-COMMIT
vendored
Normal file
@@ -0,0 +1 @@
|
||||
496720b7cfb6574a8f6f4d434f23e3d1e6cfaeb9
|
||||
1
third_party/bpftool/libbpf/CHECKPOINT-COMMIT
vendored
Normal file
1
third_party/bpftool/libbpf/CHECKPOINT-COMMIT
vendored
Normal file
@@ -0,0 +1 @@
|
||||
a3e7e6b17946f48badce98d7ac360678a0ea7393
|
||||
1
third_party/bpftool/libbpf/LICENSE
vendored
Normal file
1
third_party/bpftool/libbpf/LICENSE
vendored
Normal file
@@ -0,0 +1 @@
|
||||
LGPL-2.1 OR BSD-2-Clause
|
||||
32
third_party/bpftool/libbpf/LICENSE.BSD-2-Clause
vendored
Normal file
32
third_party/bpftool/libbpf/LICENSE.BSD-2-Clause
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
Valid-License-Identifier: BSD-2-Clause
|
||||
SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
|
||||
Usage-Guide:
|
||||
To use the BSD 2-clause "Simplified" License put the following SPDX
|
||||
tag/value pair into a comment according to the placement guidelines in
|
||||
the licensing rules documentation:
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
License-Text:
|
||||
|
||||
Copyright (c) 2015 The Libbpf Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
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.
|
||||
503
third_party/bpftool/libbpf/LICENSE.LGPL-2.1
vendored
Normal file
503
third_party/bpftool/libbpf/LICENSE.LGPL-2.1
vendored
Normal file
@@ -0,0 +1,503 @@
|
||||
Valid-License-Identifier: LGPL-2.1
|
||||
Valid-License-Identifier: LGPL-2.1+
|
||||
SPDX-URL: https://spdx.org/licenses/LGPL-2.1.html
|
||||
Usage-Guide:
|
||||
To use this license in source code, put one of the following SPDX
|
||||
tag/value pairs into a comment according to the placement
|
||||
guidelines in the licensing rules documentation.
|
||||
For 'GNU Lesser General Public License (LGPL) version 2.1 only' use:
|
||||
SPDX-License-Identifier: LGPL-2.1
|
||||
For 'GNU Lesser General Public License (LGPL) version 2.1 or any later
|
||||
version' use:
|
||||
SPDX-License-Identifier: LGPL-2.1+
|
||||
License-Text:
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts as
|
||||
the successor of the GNU Library Public License, version 2, hence the
|
||||
version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom to
|
||||
share and change it. By contrast, the GNU General Public Licenses are
|
||||
intended to guarantee your freedom to share and change free software--to
|
||||
make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some specially
|
||||
designated software packages--typically libraries--of the Free Software
|
||||
Foundation and other authors who decide to use it. You can use it too, but
|
||||
we suggest you first think carefully about whether this license or the
|
||||
ordinary General Public License is the better strategy to use in any
|
||||
particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use, not
|
||||
price. Our General Public Licenses are designed to make sure that you have
|
||||
the freedom to distribute copies of free software (and charge for this
|
||||
service if you wish); that you receive source code or can get it if you
|
||||
want it; that you can change the software and use pieces of it in new free
|
||||
programs; and that you are informed that you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis or for
|
||||
a fee, you must give the recipients all the rights that we gave you. You
|
||||
must make sure that they, too, receive or can get the source code. If you
|
||||
link other code with the library, you must provide complete object files to
|
||||
the recipients, so that they can relink them with the library after making
|
||||
changes to the library and recompiling it. And you must show them these
|
||||
terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that there is no
|
||||
warranty for the free library. Also, if the library is modified by someone
|
||||
else and passed on, the recipients should know that what they have is not
|
||||
the original version, so that the original author's reputation will not be
|
||||
affected by problems that might be introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of any
|
||||
free program. We wish to make sure that a company cannot effectively
|
||||
restrict the users of a free program by obtaining a restrictive license
|
||||
from a patent holder. Therefore, we insist that any patent license obtained
|
||||
for a version of the library must be consistent with the full freedom of
|
||||
use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||
General Public License. This license, the GNU Lesser General Public
|
||||
License, applies to certain designated libraries, and is quite different
|
||||
from the ordinary General Public License. We use this license for certain
|
||||
libraries in order to permit linking those libraries into non-free
|
||||
programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using a
|
||||
shared library, the combination of the two is legally speaking a combined
|
||||
work, a derivative of the original library. The ordinary General Public
|
||||
License therefore permits such linking only if the entire combination fits
|
||||
its criteria of freedom. The Lesser General Public License permits more lax
|
||||
criteria for linking other code with the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it does
|
||||
Less to protect the user's freedom than the ordinary General Public
|
||||
License. It also provides other free software developers Less of an
|
||||
advantage over competing non-free programs. These disadvantages are the
|
||||
reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to encourage
|
||||
the widest possible use of a certain library, so that it becomes a de-facto
|
||||
standard. To achieve this, non-free programs must be allowed to use the
|
||||
library. A more frequent case is that a free library does the same job as
|
||||
widely used non-free libraries. In this case, there is little to gain by
|
||||
limiting the free library to free software only, so we use the Lesser
|
||||
General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free programs
|
||||
enables a greater number of people to use a large body of free
|
||||
software. For example, permission to use the GNU C Library in non-free
|
||||
programs enables many more people to use the whole GNU operating system, as
|
||||
well as its variant, the GNU/Linux operating system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the users'
|
||||
freedom, it does ensure that the user of a program that is linked with the
|
||||
Library has the freedom and the wherewithal to run that program using a
|
||||
modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification
|
||||
follow. Pay close attention to the difference between a "work based on the
|
||||
library" and a "work that uses the library". The former contains code
|
||||
derived from the library, whereas the latter must be combined with the
|
||||
library in order to run.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other program
|
||||
which contains a notice placed by the copyright holder or other
|
||||
authorized party saying it may be distributed under the terms of this
|
||||
Lesser General Public License (also called "this License"). Each
|
||||
licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work which
|
||||
has been distributed under these terms. A "work based on the Library"
|
||||
means either the Library or any derivative work under copyright law:
|
||||
that is to say, a work containing the Library or a portion of it, either
|
||||
verbatim or with modifications and/or translated straightforwardly into
|
||||
another language. (Hereinafter, translation is included without
|
||||
limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for making
|
||||
modifications to it. For a library, complete source code means all the
|
||||
source code for all modules it contains, plus any associated interface
|
||||
definition files, plus the scripts used to control compilation and
|
||||
installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of running
|
||||
a program using the Library is not restricted, and output from such a
|
||||
program is covered only if its contents constitute a work based on the
|
||||
Library (independent of the use of the Library in a tool for writing
|
||||
it). Whether that is true depends on what the Library does and what the
|
||||
program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's complete
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the notices
|
||||
that refer to this License and to the absence of any warranty; and
|
||||
distribute a copy of this License along with the Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion of it,
|
||||
thus forming a work based on the Library, and copy and distribute such
|
||||
modifications or work under the terms of Section 1 above, provided that
|
||||
you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices stating
|
||||
that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no charge to
|
||||
all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a table
|
||||
of data to be supplied by an application program that uses the
|
||||
facility, other than as an argument passed when the facility is
|
||||
invoked, then you must make a good faith effort to ensure that, in
|
||||
the event an application does not supply such function or table, the
|
||||
facility still operates, and performs whatever part of its purpose
|
||||
remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has a
|
||||
purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must be
|
||||
optional: if the application does not supply it, the square root
|
||||
function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library, and
|
||||
can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based on
|
||||
the Library, the distribution of the whole must be on the terms of this
|
||||
License, whose permissions for other licensees extend to the entire
|
||||
whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of a
|
||||
storage or distribution medium does not bring the other work under the
|
||||
scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so that
|
||||
they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in these
|
||||
notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for that
|
||||
copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of the
|
||||
Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or derivative of
|
||||
it, under Section 2) in object code or executable form under the terms
|
||||
of Sections 1 and 2 above provided that you accompany it with the
|
||||
complete corresponding machine-readable source code, which must be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy from a
|
||||
designated place, then offering equivalent access to copy the source
|
||||
code from the same place satisfies the requirement to distribute the
|
||||
source code, even though third parties are not compelled to copy the
|
||||
source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the Library, but
|
||||
is designed to work with the Library by being compiled or linked with
|
||||
it, is called a "work that uses the Library". Such a work, in isolation,
|
||||
is not a derivative work of the Library, and therefore falls outside the
|
||||
scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library creates
|
||||
an executable that is a derivative of the Library (because it contains
|
||||
portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License. Section 6
|
||||
states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is
|
||||
not. Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data structure
|
||||
layouts and accessors, and small macros and small inline functions (ten
|
||||
lines or less in length), then the use of the object file is
|
||||
unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section
|
||||
6. Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or link a
|
||||
"work that uses the Library" with the Library to produce a work
|
||||
containing portions of the Library, and distribute that work under terms
|
||||
of your choice, provided that the terms permit modification of the work
|
||||
for the customer's own use and reverse engineering for debugging such
|
||||
modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work during
|
||||
execution displays copyright notices, you must include the copyright
|
||||
notice for the Library among them, as well as a reference directing the
|
||||
user to the copy of this License. Also, you must do one of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding machine-readable
|
||||
source code for the Library including whatever changes were used in
|
||||
the work (which must be distributed under Sections 1 and 2 above);
|
||||
and, if the work is an executable linked with the Library, with the
|
||||
complete machine-readable "work that uses the Library", as object
|
||||
code and/or source code, so that the user can modify the Library and
|
||||
then relink to produce a modified executable containing the modified
|
||||
Library. (It is understood that the user who changes the contents of
|
||||
definitions files in the Library will not necessarily be able to
|
||||
recompile the application to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a copy
|
||||
of the library already present on the user's computer system, rather
|
||||
than copying library functions into the executable, and (2) will
|
||||
operate properly with a modified version of the library, if the user
|
||||
installs one, as long as the modified version is interface-compatible
|
||||
with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at least three
|
||||
years, to give the same user the materials specified in Subsection
|
||||
6a, above, for a charge no more than the cost of performing this
|
||||
distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy from a
|
||||
designated place, offer equivalent access to copy the above specified
|
||||
materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these materials
|
||||
or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the Library"
|
||||
must include any data and utility programs needed for reproducing the
|
||||
executable from it. However, as a special exception, the materials to be
|
||||
distributed need not include anything that is normally distributed (in
|
||||
either source or binary form) with the major components (compiler,
|
||||
kernel, and so on) of the operating system on which the executable runs,
|
||||
unless that component itself accompanies the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license restrictions
|
||||
of other proprietary libraries that do not normally accompany the
|
||||
operating system. Such a contradiction means you cannot use both them
|
||||
and the Library together in an executable that you distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the Library
|
||||
side-by-side in a single library together with other library facilities
|
||||
not covered by this License, and distribute such a combined library,
|
||||
provided that the separate distribution of the work based on the Library
|
||||
and of the other library facilities is otherwise permitted, and provided
|
||||
that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based on
|
||||
the Library, uncombined with any other library facilities. This must
|
||||
be distributed under the terms of the Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact that part
|
||||
of it is a work based on the Library, and explaining where to find
|
||||
the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute the
|
||||
Library except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense, link with, or distribute the
|
||||
Library is void, and will automatically terminate your rights under this
|
||||
License. However, parties who have received copies, or rights, from you
|
||||
under this License will not have their licenses terminated so long as
|
||||
such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not signed
|
||||
it. However, nothing else grants you permission to modify or distribute
|
||||
the Library or its derivative works. These actions are prohibited by law
|
||||
if you do not accept this License. Therefore, by modifying or
|
||||
distributing the Library (or any work based on the Library), you
|
||||
indicate your acceptance of this License to do so, and all its terms and
|
||||
conditions for copying, distributing or modifying the Library or works
|
||||
based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted
|
||||
herein. You are not responsible for enforcing compliance by third
|
||||
parties with this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent license
|
||||
would not permit royalty-free redistribution of the Library by all
|
||||
those who receive copies directly or indirectly through you, then the
|
||||
only way you could satisfy both it and this License would be to refrain
|
||||
entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply, and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is implemented
|
||||
by public license practices. Many people have made generous
|
||||
contributions to the wide range of software distributed through that
|
||||
system in reliance on consistent application of that system; it is up
|
||||
to the author/donor to decide if he or she is willing to distribute
|
||||
software through any other system and a licensee cannot impose that
|
||||
choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in certain
|
||||
countries either by patents or by copyrighted interfaces, the original
|
||||
copyright holder who places the Library under this License may add an
|
||||
explicit geographical distribution limitation excluding those
|
||||
countries, so that distribution is permitted only in or among countries
|
||||
not thus excluded. In such case, this License incorporates the
|
||||
limitation as if written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new versions of
|
||||
the Lesser General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in
|
||||
detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a license
|
||||
version number, you may choose any version ever published by the Free
|
||||
Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free Software
|
||||
Foundation; we sometimes make exceptions for this. Our decision will be
|
||||
guided by the two goals of preserving the free status of all
|
||||
derivatives of our free software and of promoting the sharing and reuse
|
||||
of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
|
||||
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH
|
||||
YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
|
||||
NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
|
||||
DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
|
||||
DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY
|
||||
(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
|
||||
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
|
||||
THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR
|
||||
OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the library's name and an idea of what it does.
|
||||
Copyright (C) year name of author
|
||||
|
||||
This library is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this library; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add
|
||||
information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||
the library `Frob' (a library for tweaking knobs) written
|
||||
by James Random Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
That's all there is to it!
|
||||
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-compact-darkbg.png
vendored
Normal file
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-compact-darkbg.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 262 KiB |
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-compact-mono.png
vendored
Normal file
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-compact-mono.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-compact.png
vendored
Normal file
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-compact.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-sideways-darkbg.png
vendored
Normal file
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-sideways-darkbg.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 284 KiB |
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-sideways-mono.png
vendored
Normal file
BIN
third_party/bpftool/libbpf/assets/libbpf-logo-sideways-mono.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user