Repository navigation
Conversation
TDwarfExpressionPattern::Matches() kept comparing pattern operations after the expression ran out. A DWARFExpression end iterator still holds the last decoded operation, so a pattern that repeats the last operation matched a shorter expression: [breg7 8, deref, deref] matched [breg7 8, deref]. None of the current CFA patterns repeat their last operation, so this does not change the unwind tables built today, but it makes adding patterns that share a prefix safe. Add unit tests for the matcher.
Two kinds of frames keep the caller's stack pointer in memory and could
not be unwound: the stack stopped at them.
- GCC dynamic stack realignment (DRAP), used for functions with
over-aligned locals that pass arguments on the stack or use VLAs:
CFA = *(rbp - 8)
rbp = *(rbp)
Common in GCC-built code: 259 functions in Debian's libhs, 345 in
libx265.
- Hand-written OpenSSL perlasm (sha*_block_data_order_avx2 and others):
CFA = *(rsp + offset) + 8
The unwind table builder already recognized the OpenSSL form, but the
rule never reached the eBPF unwinder. Now:
- ehframe: match *(reg + offset) without a bias as well, and
DW_OP_bregN offset as a register location, which describes rbp saved
at rbp;
- unwindtable: emit UNWIND_RULE_REGISTER_DEREF_OFFSET (bias 0) and
UNWIND_RULE_REGISTER_DEREF_OFFSET_PLUS_8. The bias is encoded in the
kind because struct cfa_unwind_rule has no room for it; other biases
stay unsupported. Emit DWARF_UNWIND_RBP_RULE_AT_RBP for rbp saved at
rbp;
- eBPF: evaluate both deref kinds, rejecting a CFA that is not above the
current stack pointer since it comes from memory, and restore rbp from
*(rbp) for DWARF_UNWIND_RBP_RULE_AT_RBP.
UNWIND_RULE_REGISTER_DERREF_OFFSET was unused and is renamed to fix the
typo.
✅ All contributors are covered under a CLA with YandexSee CONTRIBUTING.md for more info about Yandex Contributor License Agreement. The following contributors were found: (Only the first commit for a unique contributor is listed) |
This was referenced Nov 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #129
Problem
Stacks stop at frames that keep the caller's stack pointer in memory. Two kinds are common:
GCC dynamic stack realignment (DRAP). Functions with over-aligned locals (e.g.
__m256i) that alsopass arguments on the stack or use VLAs get this prologue:
This is widespread in GCC-built code. Counting
DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): N; DW_OP_deref)entries: 259 in Debian's
libhs(Hyperscan), 345 inlibx265, 4,023 across/usr/binand/usr/lib/x86_64-linux-gnuon the test host.Hand-written OpenSSL perlasm (
sha*_block_data_order_avx2and others):ehframe.cppalready recognized the OpenSSL form asRegisterDerefOffset, butfillRuleturned it intoUNWIND_RULE_UNSUPPORTEDand the eBPF unwinder had no code for it. The DRAP form was not recognized atall, and neither was its rbp rule, so even with the CFA fixed the next frame would read a wrong rbp.
Fix
ehframe.cpp: match*(reg + offset)without a bias as well as with one, andDW_OP_bregN offsetas a register location, which describes rbp saved at rbp.
unwindtable: emitUNWIND_RULE_REGISTER_DEREF_OFFSET(bias 0) andUNWIND_RULE_REGISTER_DEREF_OFFSET_PLUS_8.struct cfa_unwind_rulehas no room for a bias, so it isencoded in the kind; other biases stay unsupported. Emit
DWARF_UNWIND_RBP_RULE_AT_RBPfor rbp savedat rbp.
above the current stack pointer is rejected. Restore rbp from
*(rbp)forDWARF_UNWIND_RBP_RULE_AT_RBP.UNWIND_RULE_REGISTER_DERREF_OFFSETwas unused; renamed to fix the typo.The unwind table layout is unchanged.
Testing
agent/preprocessing/lib/ut/ehframe_ut.cpp: the OpenSSL, DRAP and glibc (*(rsp + 160)) CFAexpressions, the rbp location, and expressions that must not match.
agent/collector/pkg/dso/bpf/unwindtable/manager_test.go:fillRulefor both biases, an unsupportedbias, and the rbp rule.
perforator record -p <pid> -F 999 -d 15s, before and after the change.Repro
repro-b.c+repro-b-asm.S,gcc -O2 -g -fno-omit-frame-pointer:repro-b.c
repro-b-asm.S.txt
main -> drap_caller -> drap_frame -> drap_leaf:drap_framehas a 32-byte aligned local and callsdrap_leafwith 8 arguments, so GCC emits the DRAP prologue above.main -> deref8_caller -> deref8_frame -> deref8_leaf:deref8_frameis assembly with the OpenSSLCFI,
CFA = *(rsp + 8) + 8.The callers use frame pointers, so reaching them also checks the restored rbp.
mainmain