This commit is contained in:
yunwei37
2024-10-03 03:31:55 +00:00
parent 252625d27c
commit 6f7bc37a6a
353 changed files with 165801 additions and 0 deletions

1
.mdbookignore Normal file
View File

@@ -0,0 +1 @@
third_party

10
43-kfuncs/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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 modules 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
View File

@@ -0,0 +1,2 @@
/target
Cargo.lock

1
third_party/blazesym/.rustfmt.toml vendored Normal file
View File

@@ -0,0 +1 @@
blank_lines_upper_bound = 2

38
third_party/blazesym/Cargo.toml vendored Normal file
View 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
View 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
View 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
View 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

Binary file not shown.

22
third_party/blazesym/data/test-gsym.c vendored Normal file
View 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
View 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
View 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;
}

View 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");
}
}

View 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");
}
}

View 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 = '&nbsp;&nbsp;......&lt;&lt;EXAMPLE&gt;&gt......<br/>'
self.replace_depth = 0
self.replace_start = self.getpos();
elif 'class' in attrs and 'item-decl' in attrs['class'].split():
self.replacing = '&nbsp;&nbsp;......&lt;&lt;DECLARATION&gt;&gt;......<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
View 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/&lt;pid&gt;/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

File diff suppressed because it is too large Load Diff

View 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;

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}
}
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

444
third_party/blazesym/src/util.rs vendored Normal file
View 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
View File

@@ -0,0 +1 @@
src/Makefile.* linguist-language=Makefile

3
third_party/bpftool/.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "libbpf"]
path = libbpf
url = https://github.com/libbpf/libbpf.git

View File

@@ -0,0 +1 @@
496720b7cfb6574a8f6f4d434f23e3d1e6cfaeb9

1
third_party/bpftool/CHECKPOINT-COMMIT vendored Normal file
View File

@@ -0,0 +1 @@
a3e7e6b17946f48badce98d7ac360678a0ea7393

39
third_party/bpftool/Dockerfile vendored Normal file
View 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
View File

@@ -0,0 +1 @@
GPL-2.0-only OR BSD-2-Clause

View 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
View 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.

File diff suppressed because it is too large Load Diff

2
third_party/bpftool/docs/.gitignore vendored Normal file
View 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
View 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
View 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

View 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

View 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
View 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);
...

View 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

View 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
View 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
View 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:

View 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}]

View 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

View 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
View 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.

View 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.

View 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** }

View 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

View 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

View 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)))

View 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

View 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
View 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

View 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

View 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
View 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

View 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

View 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

View 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

View 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 */

View 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 */

File diff suppressed because it is too large Load Diff

View 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__ */

View 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__ */

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -0,0 +1 @@
assets/** export-ignore

View 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

View File

@@ -0,0 +1 @@
496720b7cfb6574a8f6f4d434f23e3d1e6cfaeb9

View File

@@ -0,0 +1 @@
a3e7e6b17946f48badce98d7ac360678a0ea7393

1
third_party/bpftool/libbpf/LICENSE vendored Normal file
View File

@@ -0,0 +1 @@
LGPL-2.1 OR BSD-2-Clause

View 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.

View 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!

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

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