Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: add uprobe_multi support #252

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/test-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ jobs:
victim: ./victim
syscall_trace: false
expected_str: "bpf:"

- path: uprobe_multi
executable: ./uprobe_multi
victim: ./victim
syscall_trace: false
expected_str: "Uretprobe triggered:"
steps:
- name: Download prebuilt runtime (with jit)
if: ${{matrix.enable_jit}}
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/test-runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ jobs:
- name: build runtime with mpk enable
run: |
rm -rf build
cmake -Bbuild -DBPFTIME_LLVM_JIT=YES -DBPFTIME_ENABLE_UNIT_TESTING=YES -DBPFTIME_ENABLE_MPK=YES -DCMAKE_BUILD_TYPE=Debug
cmake --build build --config Debug --target bpftime_runtime_tests -j$(nproc)
cmake -Bbuild -DBPFTIME_LLVM_JIT=YES -DBPFTIME_ENABLE_UNIT_TESTING=YES -DBPFTIME_ENABLE_MPK=YES -DCMAKE_BUILD_TYPE=MinSizeRel
cmake --build build --config MinSizeRel --target bpftime_runtime_tests -j$(nproc)


- name: test runtime with mpk
# We don't have enough disk space to run this
if: ${{matrix.container != 'fedora-39'}}
run: ./build/runtime/unit-test/bpftime_runtime_tests
2 changes: 1 addition & 1 deletion attach/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG)
add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE)
else()
add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO)
endif()
Expand Down
5 changes: 5 additions & 0 deletions attach/base_attach_impl/attach_private_data.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef _BPFTIME_ATTACH_PRIVATE_DATA_HPP
#define _BPFTIME_ATTACH_PRIVATE_DATA_HPP

#include <string>
#include <string_view>
#include "spdlog/spdlog.h"
#include <stdexcept>
Expand All @@ -23,6 +24,10 @@ struct attach_private_data {
throw std::runtime_error(
"attach_private_data::initialize_from_string");
}
virtual std::string to_string()
{
return "<Not implemented yet>";
}
};
} // namespace attach
} // namespace bpftime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,12 @@ class frida_attach_entry {
frida_attach_entry &operator=(const frida_attach_entry &) = delete;
// Create this attach entry with its id, callback function, and function
// address to hook
frida_attach_entry(int id, frida_attach_entry_callback &&cb, void *function)
frida_attach_entry(int id, frida_attach_entry_callback &&cb,
void *function)
: self_id(id), callback(cb), function(function)
{
SPDLOG_DEBUG("Creating frida attach entry at addr 0x{:x}",
(uintptr_t)function);
}
frida_attach_entry(frida_attach_entry &&) = default;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ namespace attach
{
// Private data for frida uprobe attach
struct frida_attach_private_data final : public attach_private_data {
// The address to hook
// The address to hook
uint64_t addr;
// The input string should be: Either an decimal integer in string format, indicating the function address to hook. Or in format of NAME:OFFSET, where NAME is the module name (empty is ok), OFFSET is the module offset
// The input string should be: Either an decimal integer in string
// format, indicating the function address to hook. Or in format of
// NAME:OFFSET, where NAME is the module name (empty is ok), OFFSET is
// the module offset
int initialize_from_string(const std::string_view &sv) override;
std::string to_string() override;
};
} // namespace attach
} // namespace bpftime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@ int frida_attach_private_data::initialize_from_string(const std::string_view &sv

return 0;
}

std::string frida_attach_private_data::to_string()
{
return std::string("<Frida attach private data addr=") +
std::to_string(addr);
}
13 changes: 11 additions & 2 deletions attach/frida_uprobe_attach_impl/src/frida_attach_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,24 @@ resolve_function_addr_by_module_offset(const std::string_view &module_name,
{
auto exec_path = get_executable_path();
void *module_base_addr = nullptr;
SPDLOG_DEBUG("Resolving module base addr, module name {}, exec_path {}",
module_name, exec_path);
if (std::filesystem::equivalent(module_name, exec_path)) {
SPDLOG_DEBUG(
"module name {} is equivalent to exec path {}, using empty string to resolve module base addr",
module_name, exec_path);
module_base_addr = get_module_base_addr("");
} else {
SPDLOG_DEBUG(
"module name {} is *not* equivalent to exec path {}, using module name to resolve module base addr",
module_name, exec_path);
module_base_addr =
get_module_base_addr(std::string(module_name).c_str());
}
if (!module_base_addr) {
SPDLOG_ERROR("Failed to find module base address for {}",
module_name);
SPDLOG_ERROR(
"Failed to find module base address for {}, cwd = {}",
module_name, std::filesystem::current_path().c_str());
return nullptr;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ frida_internal_attach_entry::frida_internal_attach_entry(
});
}
this->interceptor = gum_object_ref(interceptor);
SPDLOG_DEBUG("Initialized frida internal attach entry at {:x}",
(uintptr_t)function);
}

frida_internal_attach_entry::~frida_internal_attach_entry()
Expand Down
12 changes: 12 additions & 0 deletions example/uprobe_multi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.vscode
package.json
*.o
*.skel.json
*.skel.yaml
package.yaml
ecli
uprobe_multi
.output
test
victim
dump*
141 changes: 141 additions & 0 deletions example/uprobe_multi/Makefile
Original file line number Diff line number Diff line change
@@ -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
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 = uprobe_multi # 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) victim

victim: victim.c
gcc victim.c -o victim -Wall -g

.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:
3 changes: 3 additions & 0 deletions example/uprobe_multi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# uprobe_multi example

This is a simple uprobe_multi example
39 changes: 39 additions & 0 deletions example/uprobe_multi/uprobe_multi.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#define BPF_NO_GLOBAL_DATA
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "uprobe_multi.h"

struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} rb SEC(".maps");

SEC("uprobe.multi/./victim:uprobe_multi_func_*")
int uprobe_multi_test(struct pt_regs *ctx)
{
struct uprobe_multi_event *event;
event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
if (!event)
return 0;
event->is_ret = 0;
event->uprobe.arg1 = (long)ctx->di;
event->uprobe.arg2 = (long)ctx->si;
bpf_ringbuf_submit(event, 0);
return 0;
}

SEC("uretprobe.multi/./victim:uprobe_multi_func_*")
int uretprobe_multi_test(struct pt_regs *ctx)
{
struct uprobe_multi_event *event;
event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
if (!event)
return 0;
event->is_ret = 1;
event->uretprobe.ret_val = (long)ctx->ax;
bpf_ringbuf_submit(event, 0);
return 0;
}

char _license[] SEC("license") = "GPL";
Loading
Loading