Skip to content

Commit

Permalink
Find instruction before return address
Browse files Browse the repository at this point in the history
As we don't want the return address as it points to the next instruction
after the function call. See comments for implementation details for
different architectures.

The kernel unwinder seems to have the same issue, so this is something
that should be adjusted in userspace to avoid using more time in the
unwinder. This will be fixed later on.

Test Plan
=========

Ran a bunch of tests locally and some of the previous off-by-ones while
symbolising are gone.
  • Loading branch information
javierhonduco committed Feb 16, 2025
1 parent 4b389c0 commit cbc0139
Showing 1 changed file with 17 additions and 4 deletions.
21 changes: 17 additions & 4 deletions src/bpf/profiler.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,26 @@ static __always_inline void send_event(Event *event, struct bpf_perf_event_data
bpf_map_update_elem(&rate_limits, event, &rate_limited, BPF_ANY);
}

// The return address points as the the instruction at which execution
// will resume after returning from a function call, we need to get the
// previous instruction's address.
static __always_inline u64 previous_instruction_addr(u64 addr) {
#ifdef __TARGET_ARCH_x86
// On x86 it's not possible to find the previous instruction address
// without fully disassembling the whole executable from the start.
// By substracting 1 byte, we make sure to fall within the previous
// instruction.
return addr - 1;
#elif __TARGET_ARCH_arm64
return addr - 4;
#endif
}

#ifdef __TARGET_ARCH_x86
static __always_inline u64 remove_pac(u64 addr) {
return addr;
}
#endif

#ifdef __TARGET_ARCH_arm64
#elif __TARGET_ARCH_arm64
// Arm64 supports pointer authentication, we need to remove the signatured during
// unwinding.
static __always_inline u64 remove_pac(u64 addr) {
Expand Down Expand Up @@ -615,7 +628,7 @@ int dwarf_unwind(struct bpf_perf_event_data *ctx) {
LOG("\tprevious ip: %llx (@ %llx)", previous_rip, previous_rip_addr);
LOG("\tprevious sp: %llx", previous_rsp);
// Set rsp and rip registers
unwind_state->ip = remove_pac(previous_rip);
unwind_state->ip = previous_instruction_addr(remove_pac(previous_rip));
unwind_state->sp = previous_rsp;
// Set rbp
LOG("\tprevious bp: %llx", previous_rbp);
Expand Down

0 comments on commit cbc0139

Please sign in to comment.