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

Add support for dwarf expressions for PLTs #1058

Merged
merged 2 commits into from
Nov 22, 2022
Merged

Conversation

javierhonduco
Copy link
Contributor

@javierhonduco javierhonduco commented Nov 22, 2022

More context and rationale in the commit message, but this PR adds support for a small subset of DWARF expressions to compute the CFA. In particular, this tackles the common Procedure Linkage Tables (PLT). See this great post by MaskRay.

Bear in mind that while now we can theoretically add more hardcoded expressions I think we should be mindful of the increase in code size they can cause, so we ideally limit the number of hardcoded (or not) expressions.

Test Plan

Added a basic unittest to ensure that we don't accidentally change the dwarf expression constants (which were wrong in the Delve project. Will upstream this in some days)

No errors in both:
basic-cpp-plt

basic-cpp-plt-38333   [009] d.h2. 12769.059697: bpf_trace_printk: unsup_expression=0
basic-cpp-plt-38333   [009] d.h2. 12769.959741: bpf_trace_printk: unsup_expression=0
basic-cpp-plt-38333   [009] d.h2. 12770.979792: bpf_trace_printk: unsup_expression=0
basic-cpp-plt-38333   [009] d.h2. 12771.839835: bpf_trace_printk: unsup_expression=0
basic-cpp-plt-38333   [009] d.h2. 12772.279861: bpf_trace_printk: CFA expression found with id 1
basic-cpp-plt-38333   [009] d.h2. 12772.529869: bpf_trace_printk: unsup_expression=0
basic-cpp-plt-38333   [009] d.h2. 12773.409912: bpf_trace_printk: unsup_expression=0
basic-cpp-plt-38333   [009] d.h2. 12774.129948: bpf_trace_printk: unsup_expression=0
basic-cpp-plt-38333   [009] d.h2. 12774.929988: bpf_trace_printk: unsup_expression=0

Whole debug log:

   basic-cpp-plt-91086   [009] d.h2. 16954.316196: bpf_trace_printk: ## frame: 0
   basic-cpp-plt-91086   [009] d.h2. 16954.316197: bpf_trace_printk:    current pc: 401030
   basic-cpp-plt-91086   [009] d.h2. 16954.316197: bpf_trace_printk:    current sp: 7ffe1ba364b8
   basic-cpp-plt-91086   [009] d.h2. 16954.316197: bpf_trace_printk:    current bp: 7ffe1ba364c0
   basic-cpp-plt-91086   [009] d.h2. 16954.316197: bpf_trace_printk:     Shard 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316198: bpf_trace_printk:    .done
   basic-cpp-plt-91086   [009] d.h2. 16954.316198: bpf_trace_printk:    => table_index: 2
   basic-cpp-plt-91086   [009] d.h2. 16954.316200: bpf_trace_printk:    cfa type: $, offset: 1 (row pc: 401030)
   basic-cpp-plt-91086   [009] d.h2. 16954.316201: bpf_trace_printk: CFA expression found with id 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316201: bpf_trace_printk:    previous ip: 4011b8 (@ 7ffe1ba364b8)
   basic-cpp-plt-91086   [009] d.h2. 16954.316202: bpf_trace_printk:    previous sp: 7ffe1ba364c0
   basic-cpp-plt-91086   [009] d.h2. 16954.316202: bpf_trace_printk:    previous bp: 7ffe1ba364c0
   basic-cpp-plt-91086   [009] d.h2. 16954.316202: bpf_trace_printk: ## frame: 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316202: bpf_trace_printk:    current pc: 4011b8
   basic-cpp-plt-91086   [009] d.h2. 16954.316203: bpf_trace_printk:    current sp: 7ffe1ba364c0
   basic-cpp-plt-91086   [009] d.h2. 16954.316203: bpf_trace_printk:    current bp: 7ffe1ba364c0
   basic-cpp-plt-91086   [009] d.h2. 16954.316203: bpf_trace_printk:     Shard 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316204: bpf_trace_printk:    .done
   basic-cpp-plt-91086   [009] d.h2. 16954.316204: bpf_trace_printk:    => table_index: 40
   basic-cpp-plt-91086   [009] d.h2. 16954.316205: bpf_trace_printk:    cfa type: $, offset: 16 (row pc: 4011ae)
   basic-cpp-plt-91086   [009] d.h2. 16954.316205: bpf_trace_printk:    (bp_offset: -16, bp value stored at 7ffe1ba364c0)
   basic-cpp-plt-91086   [009] d.h2. 16954.316206: bpf_trace_printk:    previous ip: 7f6f7e0c1510 (@ 7ffe1ba364c8)
   basic-cpp-plt-91086   [009] d.h2. 16954.316206: bpf_trace_printk:    previous sp: 7ffe1ba364d0
   basic-cpp-plt-91086   [009] d.h2. 16954.316206: bpf_trace_printk:    previous bp: 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316207: bpf_trace_printk: ## frame: 2
   basic-cpp-plt-91086   [009] d.h2. 16954.316207: bpf_trace_printk:    current pc: 7f6f7e0c1510
   basic-cpp-plt-91086   [009] d.h2. 16954.316207: bpf_trace_printk:    current sp: 7ffe1ba364d0
   basic-cpp-plt-91086   [009] d.h2. 16954.316207: bpf_trace_printk:    current bp: 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316208: bpf_trace_printk:     Shard 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316208: bpf_trace_printk:    .done
   basic-cpp-plt-91086   [009] d.h2. 16954.316208: bpf_trace_printk:    => table_index: 170
   basic-cpp-plt-91086   [009] d.h2. 16954.316209: bpf_trace_printk:    cfa type: $, offset: 160 (row pc: 7f6f7e0c1499)
   basic-cpp-plt-91086   [009] d.h2. 16954.316210: bpf_trace_printk:    previous ip: 7f6f7e0c15c9 (@ 7ffe1ba36568)
   basic-cpp-plt-91086   [009] d.h2. 16954.316210: bpf_trace_printk:    previous sp: 7ffe1ba36570
   basic-cpp-plt-91086   [009] d.h2. 16954.316210: bpf_trace_printk:    previous bp: 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316210: bpf_trace_printk: ## frame: 3
   basic-cpp-plt-91086   [009] d.h2. 16954.316211: bpf_trace_printk:    current pc: 7f6f7e0c15c9
   basic-cpp-plt-91086   [009] d.h2. 16954.316211: bpf_trace_printk:    current sp: 7ffe1ba36570
   basic-cpp-plt-91086   [009] d.h2. 16954.316211: bpf_trace_printk:    current bp: 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316211: bpf_trace_printk:     Shard 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316212: bpf_trace_printk:    .done
   basic-cpp-plt-91086   [009] d.h2. 16954.316212: bpf_trace_printk:    => table_index: 178
   basic-cpp-plt-91086   [009] d.h2. 16954.316213: bpf_trace_printk:    cfa type: $, offset: 80 (row pc: 7f6f7e0c155a)
   basic-cpp-plt-91086   [009] d.h2. 16954.316213: bpf_trace_printk:    (bp_offset: -48, bp value stored at 7ffe1ba36590)
   basic-cpp-plt-91086   [009] d.h2. 16954.316214: bpf_trace_printk:    previous ip: 401065 (@ 7ffe1ba365b8)
   basic-cpp-plt-91086   [009] d.h2. 16954.316214: bpf_trace_printk:    previous sp: 7ffe1ba365c0
   basic-cpp-plt-91086   [009] d.h2. 16954.316214: bpf_trace_printk:    previous bp: 0
   basic-cpp-plt-91086   [009] d.h2. 16954.316214: bpf_trace_printk: ## frame: 4
   basic-cpp-plt-91086   [009] d.h2. 16954.316215: bpf_trace_printk:    current pc: 401065
   basic-cpp-plt-91086   [009] d.h2. 16954.316215: bpf_trace_printk:    current sp: 7ffe1ba365c0
   basic-cpp-plt-91086   [009] d.h2. 16954.316215: bpf_trace_printk:    current bp: 0
   basic-cpp-plt-91086   [009] d.h2. 16954.316215: bpf_trace_printk:     Shard 1
   basic-cpp-plt-91086   [009] d.h2. 16954.316215: bpf_trace_printk:    .done
   basic-cpp-plt-91086   [009] d.h2. 16954.316216: bpf_trace_printk:    => table_index: 4
   basic-cpp-plt-91086   [009] d.h2. 16954.316217: bpf_trace_printk:    cfa type: $, offset: 8 (row pc: 401044)
   basic-cpp-plt-91086   [009] d.h2. 16954.316217: bpf_trace_printk:    previous ip: 7ffe1ba365c8 (@ 7ffe1ba365c0)
   basic-cpp-plt-91086   [009] d.h2. 16954.316217: bpf_trace_printk:    previous sp: 7ffe1ba365c8
   basic-cpp-plt-91086   [009] d.h2. 16954.316218: bpf_trace_printk:    previous bp: 0
   basic-cpp-plt-91086   [009] d.h2. 16954.316218: bpf_trace_printk: ## frame: 5
   basic-cpp-plt-91086   [009] d.h2. 16954.316218: bpf_trace_printk:    current pc: 7ffe1ba365c8
   basic-cpp-plt-91086   [009] d.h2. 16954.316218: bpf_trace_printk:    current sp: 7ffe1ba365c8
   basic-cpp-plt-91086   [009] d.h2. 16954.316219: bpf_trace_printk:    current bp: 0
   basic-cpp-plt-91086   [009] d.h2. 16954.316219: bpf_trace_printk: ======= reached main! =======
   basic-cpp-plt-91086   [009] d.h2. 16954.316220: bpf_trace_printk: stack hash -610228879
   basic-cpp-plt-91086   [009] d.h2. 16954.316221: bpf_trace_printk: yesssss :)
            node-2224    [006] d.h2. 16954.326129: bpf_trace_printk: debug mode enabled, make sure you specified process name
            node-2152    [007] d.h2. 16954.326142: bpf_trace_printk: debug mode enabled, make sure you specified process name
   basic-cpp-plt-91086   [009] d.h2. 16954.326193: bpf_trace_printk: debug mode enabled, make sure you specified process name
   basic-cpp-plt-91086   [009] d.h2. 16954.326194: bpf_trace_printk:     Shard 1
   basic-cpp-plt-91086   [009] d.h2. 16954.326194: bpf_trace_printk: pid 91086 tgid 91086

ruby

irb-49214   [009] d.h2. 13928.213073: bpf_trace_printk: unsup_expression=0
irb-49214   [009] d.h2. 13930.013134: bpf_trace_printk: unsup_expression=0
irb-49214   [009] d.h2. 13930.703156: bpf_trace_printk: unsup_expression=0
irb-49214   [009] d.h2. 13931.293184: bpf_trace_printk: CFA expression found with id 1
irb-49214   [009] d.h2. 13931.893211: bpf_trace_printk: unsup_expression=0
irb-49214   [009] d.h2. 13932.003210: bpf_trace_printk: CFA expression found with id 1
irb-49214   [009] d.h2. 13932.623221: bpf_trace_printk: unsup_expression=0
irb-49214   [011] d.h2. 13933.180899: bpf_trace_printk: unsup_expression=0

Signed-off-by: Francisco Javier Honduvilla Coto <[email protected]>
@javierhonduco javierhonduco requested a review from a team as a code owner November 22, 2022 15:08
@javierhonduco javierhonduco force-pushed the dwarf-expressions-plt branch 3 times, most recently from f6d2551 to d9acde2 Compare November 22, 2022 15:36
Copy link
Member

@kakkoyun kakkoyun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Looks good to me. I only have comments on naming conventions. We seem to change them back and forth. We can try to find a linter setting for us to vet these if you want.

docs/native-stack-walking/design.md Outdated Show resolved Hide resolved
pkg/profiler/cpu/maps.go Show resolved Hide resolved
pkg/stack/unwind/dwarf_expression.go Show resolved Hide resolved
pkg/stack/unwind/dwarf_expression.go Show resolved Hide resolved
pkg/stack/unwind/dwarf_expression.go Outdated Show resolved Hide resolved
pkg/stack/unwind/dwarf_expression.go Outdated Show resolved Hide resolved
This commits adds partial support for DWARF expressions, in particular,
for unwind ranges that cover PLTs for the CFA calculation
(`DW_CFA_def_cfa_expression`).

Arbitrary expressions require a VM to evaluate them, which is not easy
to do in BPF, so after doing some quick data analysis (see below) it was
clear that by adding some hardcoded expressions to increase the success
ratio of the stack walker without bloating it too much. I don't think we
should add every "common" expression, but the PLT ones seemed like
good bang for the buck.

On my Fedora machine, these were the most common expressions for
binaries, and libraries, as well as for processes running:

```
[javierhonduco@fedora parca-agent]$ sudo readelf -wf /bin/* 2>/dev/null | grep DW_CFA_def_cfa_expression | sort | uniq -c | sort -k1h
      1   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 160; DW_OP_deref)
      5   DW_CFA_def_cfa_expression (DW_OP_breg4 (esp): 4; DW_OP_breg8 (eip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit9; DW_OP_ge; DW_OP_lit2; DW_OP_shl; DW_OP_plus)
      5   DW_CFA_def_cfa_expression (DW_OP_breg5 (ebp): -16; DW_OP_deref)
     12   DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -40; DW_OP_deref)
     62   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16 (rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit11; DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)
   1673   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16 (rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit10; DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)
```

```
[javierhonduco@fedora parca-agent]$ sudo readelf -wf /lib64/* 2>/dev/null | grep DW_CFA_def_cfa_expression | sort | uniq -c | sort -k1h
[...]
     47   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16 (rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit11; DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)
    237   DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -24; DW_OP_deref)
    910   DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -8; DW_OP_deref)
   1006   DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -40; DW_OP_deref)
   2563   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16 (rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit10; DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)
```

```
[javierhonduco@fedora parca-agent]$ sudo readelf -wf /proc/*/exe 2>/dev/null | grep DW_CFA_def_cfa_expression | sort | uniq -c | sort -k1h
[...]
     25   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 120; DW_OP_deref; DW_OP_plus_uconst: 8)
     25   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 152; DW_OP_deref; DW_OP_plus_uconst: 8)
     25   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 88; DW_OP_deref; DW_OP_plus_uconst: 8)
     30   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 0; DW_OP_deref; DW_OP_plus_uconst: 8)
     35   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 40; DW_OP_deref; DW_OP_plus_uconst: 8)
     45   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): -8; DW_OP_deref; DW_OP_plus_uconst: 8)
    130   DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16 (rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit10; DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)
```

As it can be seen above, by adding PLT expression support, we can get ~50% more unwind sections working.

PLT sections are particularly important as libc is typically dynamically
linked. In this case, most libc calls go through the Procedure Linkage
Table, and before this commit, we will stop walking the stack.

See test plan in the PR.

Signed-off-by: Francisco Javier Honduvilla Coto <[email protected]>
@javierhonduco javierhonduco merged commit 1439e16 into main Nov 22, 2022
@javierhonduco javierhonduco deleted the dwarf-expressions-plt branch November 22, 2022 17:13
Sylfrena added a commit that referenced this pull request Jan 26, 2024
DWARF Expressions are encoded with a `DW_CFA_def_cfa_expression` or
`DW_CFA_expression` (as per aarch64 DWARF ABI). However
grepping for either in `.eh_frame` debug information did not show any
results (including hardcoded PLT patter heuristics) unlike in x86.
(See #1058 for the x86 implementation).

Tested on system binaries and libc in multiple distros:
Debian, Fedora, Centos, Ubuntu

Workflow:
```
`docker run -it --network=host arm64v8/fedora bash`
`docker cp 0693985d3bba:/ ./fedrepo`

`sudo readelf -wf ./fedrepo/bin/* 2>/dev/null | grep
DW_CFA_cfa_def_expression`
`sudo readelf -wF ./centos-repo/bin/* 2>/dev/null | grep exp`

```
Return an error for now if we come across an expression in Arm64 binaries.

Signed-off-by: Sumera Priyadarsini <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants