From 39e3f591195169fe98b6fc8196f0ec8d2d363723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E5=BE=AE?= <1067852565@qq.com> Date: Tue, 14 Jul 2026 03:58:41 -0700 Subject: [PATCH] ci: support Rust 1.97 and modernize workflows (#210) * Modernize workflow actions and permissions * Test the fentry tutorial in CI * Support Rust 1.97 symbol mangling * Limit documentation writes to main * Harden Rust symbol resolution --------- Co-authored-by: LinuxDev9002 --- .github/workflows/auto-generate-docs.yml | 8 +++-- .github/workflows/test-bpftrace.yml | 27 +++++++++------- .github/workflows/test-eunomia.yaml | 10 ++++-- .github/workflows/test-libbpf.yml | 6 +++- .github/workflows/trigger-sync.yml | 9 ++++-- src/37-uprobe-rust/README.md | 41 ++++++++++++------------ src/37-uprobe-rust/README.zh.md | 41 ++++++++++++------------ 7 files changed, 79 insertions(+), 63 deletions(-) diff --git a/.github/workflows/auto-generate-docs.yml b/.github/workflows/auto-generate-docs.yml index 8b16bee..9a9372d 100644 --- a/.github/workflows/auto-generate-docs.yml +++ b/.github/workflows/auto-generate-docs.yml @@ -2,6 +2,7 @@ name: Auto Generate Documentation on: push: + branches: [main] paths: - 'src/**/README.md' - 'src/**/README.zh.md' @@ -10,19 +11,22 @@ on: - 'scripts/*.template' workflow_dispatch: +permissions: + contents: write + jobs: generate-docs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: '3.x' diff --git a/.github/workflows/test-bpftrace.yml b/.github/workflows/test-bpftrace.yml index 1f4992d..fd6c387 100644 --- a/.github/workflows/test-bpftrace.yml +++ b/.github/workflows/test-bpftrace.yml @@ -7,27 +7,27 @@ on: branches: [ "main" ] schedule: - cron: '0 0 * * 0' + +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 with: submodules: 'recursive' - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true + uses: dtolnay/rust-toolchain@1.97.0 - name: test 37 uprobe-rust helloworld run: | cd src/37-uprobe-rust/helloworld cargo build # Verify the hello function symbol exists in debug build - nm target/debug/helloworld | grep "_ZN10helloworld5hello" || (echo "Error: hello function symbol not found in debug build" && exit 1) + nm -C target/debug/helloworld | grep -F "helloworld::hello" || (echo "Error: hello function symbol not found in debug build" && exit 1) echo "✓ helloworld debug build has correct hello symbol" - name: test 37 uprobe-rust args @@ -35,7 +35,7 @@ jobs: cd src/37-uprobe-rust/args cargo build # Verify the hello function symbol exists in debug build - nm target/debug/helloworld | grep "_ZN10helloworld5hello" || (echo "Error: hello function symbol not found in debug build" && exit 1) + nm -C target/debug/helloworld | grep -F "helloworld::hello" || (echo "Error: hello function symbol not found in debug build" && exit 1) echo "✓ args debug build has correct hello symbol" # Run the binary to ensure it works ./target/debug/helloworld 1 2 3 | grep -q "Hello, world!" || (echo "Error: binary execution failed" && exit 1) @@ -44,12 +44,15 @@ jobs: - name: test 37 uprobe-rust bpftrace attach run: | cd src/37-uprobe-rust/args - # Test that bpftrace can attach to the hello function with wildcard pattern - # Run bpftrace with the binary and verify it can trace the function - sudo timeout 3 bpftrace -e 'uprobe:target/debug/helloworld:_ZN10helloworld5hello* { printf("traced\n"); exit(); }' -c "./target/debug/helloworld 1" > /tmp/bpftrace_output.txt 2>&1 || true + # Rust 1.97 uses v0 mangling by default, so resolve the raw symbol dynamically. + hello_address=$(nm -C target/debug/helloworld | awk '$2 ~ /^[tT]$/ && $3 ~ /^helloworld::hello(::h[[:xdigit:]]+)?$/ && !matched { found = $1; matched = 1 } END { if (matched) print found }') + test -n "$hello_address" || (echo "Error: demangled hello function symbol not found" && exit 1) + hello_symbol=$(nm target/debug/helloworld | awk -v address="$hello_address" '$1 == address && $2 ~ /^[tT]$/ && !matched { found = $3; matched = 1 } END { if (matched) print found }') + test -n "$hello_symbol" || (echo "Error: raw hello function symbol not found" && exit 1) + sudo timeout 3 bpftrace -e "uprobe:target/debug/helloworld:${hello_symbol} { printf(\"traced\\n\"); exit(); }" -c "./target/debug/helloworld 1" > /tmp/bpftrace_output.txt 2>&1 || true # Check if bpftrace successfully attached and traced the function if grep -q "traced" /tmp/bpftrace_output.txt && grep -q "Attaching 1 probe" /tmp/bpftrace_output.txt; then - echo "✓ bpftrace successfully attached to hello function with wildcard pattern" + echo "✓ bpftrace successfully attached to hello function" else echo "Error: bpftrace failed to attach or trace the hello function" cat /tmp/bpftrace_output.txt diff --git a/.github/workflows/test-eunomia.yaml b/.github/workflows/test-eunomia.yaml index 91c7f27..68e2e24 100644 --- a/.github/workflows/test-eunomia.yaml +++ b/.github/workflows/test-eunomia.yaml @@ -7,11 +7,15 @@ on: branches: [ "main" ] schedule: - cron: '0 0 * * 0' + +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 with: submodules: 'recursive' - name: install deps @@ -30,8 +34,8 @@ jobs: sudo timeout -s 2 3 ./ecli run src/2-kprobe-unlink/package.json || if [ $? = 124 ]; then exit 0; else exit $?; fi - name: test 3 fentry run: | - ./ecc src/2-kprobe-unlink/kprobe-link.bpf.c - sudo timeout -s 2 3 ./ecli run src/2-kprobe-unlink/package.json || if [ $? = 124 ]; then exit 0; else exit $?; fi + ./ecc src/3-fentry-unlink/fentry-link.bpf.c + sudo timeout -s 2 3 ./ecli run src/3-fentry-unlink/package.json || if [ $? = 124 ]; then exit 0; else exit $?; fi - name: test 4 opensnoop run: | ./ecc src/4-opensnoop/opensnoop.bpf.c diff --git a/.github/workflows/test-libbpf.yml b/.github/workflows/test-libbpf.yml index 58bcf68..2b4be5a 100644 --- a/.github/workflows/test-libbpf.yml +++ b/.github/workflows/test-libbpf.yml @@ -7,11 +7,15 @@ on: branches: [ "main" ] schedule: - cron: '0 0 * * 0' + +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 with: submodules: 'recursive' - name: install deps diff --git a/.github/workflows/trigger-sync.yml b/.github/workflows/trigger-sync.yml index ba29fc8..8257ce6 100644 --- a/.github/workflows/trigger-sync.yml +++ b/.github/workflows/trigger-sync.yml @@ -7,18 +7,21 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + jobs: trigger: runs-on: ubuntu-latest steps: - name: Checkout website repo - uses: actions/checkout@v3 + uses: actions/checkout@v7 with: repository: ${{ github.repository_owner }}/eunomia.dev ref: main path: ./. - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v6 with: python-version: 3.x @@ -32,7 +35,7 @@ jobs: - name: Trigger sync workflow if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: peter-evans/repository-dispatch@v2 + uses: peter-evans/repository-dispatch@v4 with: token: ${{ secrets.PAT }} repository: ${{ github.repository_owner }}/eunomia.dev diff --git a/src/37-uprobe-rust/README.md b/src/37-uprobe-rust/README.md index b793417..662b44a 100644 --- a/src/37-uprobe-rust/README.md +++ b/src/37-uprobe-rust/README.md @@ -43,27 +43,23 @@ Build and try to get the symbol: ```console $ cd helloworld $ cargo build -$ nm helloworld/target/release/helloworld | grep hello -0000000000008940 t _ZN10helloworld4main17h2dce92cb81426b91E +$ nm -C target/debug/helloworld | grep 'helloworld::main' +0000000000013ec0 t helloworld::main ``` -We find that the corresponding symbol has been converted to `_ZN10helloworld4main17h2dce92cb81426b91E`. This is because rustc uses [Symbol name mangling](https://en.wikipedia.org/wiki/Name_mangling) to encode a unique name for the symbols used in the code generation process. The encoded name will be used by the linker to associate the name with the content it points to. The -C symbol-mangling-version option can be used to control the handling of symbol names. - -We can use the [`rustfilt`](https://github.com/luser/rustfilt) tool to parse and obtain the corresponding symbol. This tool can be installed with `cargo install rustfilt`: +Rustc uses [symbol name mangling](https://en.wikipedia.org/wiki/Name_mangling) to encode unique linker names. Rust 1.97 uses v0 mangling by default, while `nm -C` prints the stable demangled name. Resolve the raw symbol through its address before passing it to bpftrace: ```console -$ cargo install rustfilt -$ nm helloworld/target/release/helloworld > name.txt -$ rustfilt _ZN10helloworld4main17h2dce92cb81426b91E -helloworld::main -$ rustfilt -i name.txt | grep hello -0000000000008b60 t helloworld::main +$ main_address=$(nm -C target/debug/helloworld | awk '$2 ~ /^[tT]$/ && $3 ~ /^helloworld::main(::h[[:xdigit:]]+)?$/ && !matched { found = $1; matched = 1 } END { if (matched) print found }') +$ main_symbol=$(nm target/debug/helloworld | awk -v address="$main_address" '$1 == address && $2 ~ /^[tT]$/ && !matched { found = $3; matched = 1 } END { if (matched) print found }') +$ echo "$main_symbol" +_RNvCsiXtUZV4ocyZ_10helloworld4main ``` -Next we can try to use bpftrace to trace the corresponding function: +The `-C symbol-mangling-version` rustc option can select a mangling scheme, but resolving the current binary avoids depending on a particular scheme. We can now trace `main`: ```console -$ sudo bpftrace -e 'uprobe:helloworld/target/release/helloworld:_ZN10helloworld4main17h2dce92cb81426b91E { printf("Function hello-world called\n"); }' +$ sudo bpftrace -e "uprobe:target/debug/helloworld:${main_symbol} { printf(\"Function hello-world called\n\"); }" Attaching 1 probe... Function hello-world called ``` @@ -103,17 +99,20 @@ First, we need to build in debug mode because the `hello` function gets inlined ```console $ cd args $ cargo build -$ nm target/debug/helloworld | grep hello -0000000000016250 t _ZN10helloworld4main17ha3594bca2af541f6E -0000000000016540 t _ZN10helloworld5hello17h5f3a03dda56661e1E +$ nm -C target/debug/helloworld | grep 'helloworld::hello' +0000000000016f90 t helloworld::hello ``` Note that in release mode (`cargo build --release`), only the `main` function symbol appears because `hello` gets inlined during optimization. -Now we can trace the `hello` function using its symbol. Since Rust mangles symbol names and includes a hash that changes with each compilation, we use a wildcard pattern to match any version of the `hello` function: +Rust 1.97 uses v0 symbol mangling by default. bpftrace needs the raw symbol, so resolve it from the address of the stable demangled name: ```console -$ sudo bpftrace -e 'uprobe:target/debug/helloworld:_ZN10helloworld5hello* { printf("Function hello called\n"); }' +$ hello_address=$(nm -C target/debug/helloworld | awk '$2 ~ /^[tT]$/ && $3 ~ /^helloworld::hello(::h[[:xdigit:]]+)?$/ && !matched { found = $1; matched = 1 } END { if (matched) print found }') +$ hello_symbol=$(nm target/debug/helloworld | awk -v address="$hello_address" '$1 == address && $2 ~ /^[tT]$/ && !matched { found = $3; matched = 1 } END { if (matched) print found }') +$ echo "$hello_symbol" +_RNvCsiXtUZV4ocyZ_10helloworld5hello +$ sudo bpftrace -e "uprobe:target/debug/helloworld:${hello_symbol} { printf(\"Function hello called\n\"); }" Attaching 1 probe... Function hello called Function hello called @@ -135,10 +134,10 @@ Hello, world! 4 in 5 return value: 9 ``` -We can also get the return value using Uretprobe. Again, we use a wildcard to match the symbol: +We can also get the return value using Uretprobe and the same resolved symbol: ```console -$ sudo bpftrace -e 'uretprobe:target/debug/helloworld:_ZN10helloworld5hello* { printf("Function hello returned: %d\n", retval); }' +$ sudo bpftrace -e "uretprobe:target/debug/helloworld:${hello_symbol} { printf(\"Function hello returned: %d\n\", retval); }" Attaching 1 probe... Function hello returned: 6 Function hello returned: 7 @@ -146,7 +145,7 @@ Function hello returned: 8 Function hello returned: 9 ``` -Note: The wildcard pattern `_ZN10helloworld5hello*` matches the `hello` function symbol regardless of the hash suffix that Rust adds during compilation. You can use `nm target/debug/helloworld | grep hello` to see the exact symbol names if needed. +Resolving the raw symbol through its demangled name works with both legacy and v0 Rust mangling. ## References diff --git a/src/37-uprobe-rust/README.zh.md b/src/37-uprobe-rust/README.zh.md index 21be984..3fe9ef9 100644 --- a/src/37-uprobe-rust/README.zh.md +++ b/src/37-uprobe-rust/README.zh.md @@ -41,27 +41,23 @@ fn main() { ```console $ cd helloworld $ cargo build -$ nm helloworld/target/release/helloworld | grep hello -0000000000008940 t _ZN10helloworld4main17h2dce92cb81426b91E +$ nm -C target/debug/helloworld | grep 'helloworld::main' +0000000000013ec0 t helloworld::main ``` -我们会发现,对应的符号被转换为了 `_ZN10helloworld4main17h2dce92cb81426b91E`,这是因为 rustc 使用 [Symbol name mangling](https://en.wikipedia.org/wiki/Name_mangling) 来为代码生成过程中使用的符号编码一个唯一的名称。编码后的名称会被链接器用于将名称与所指向的内容关联起来。可以使用 -C symbol-mangling-version 选项来控制符号名称的处理方法。 - -我们可以使用 [`rustfilt`](https://github.com/luser/rustfilt) 工具来解析和获取对应的符号。这个工具可以通过 `cargo install rustfilt` 安装: +rustc 使用 [符号名称修饰](https://en.wikipedia.org/wiki/Name_mangling) 为链接器编码唯一的符号名称。Rust 1.97 默认使用 v0 名称修饰,而 `nm -C` 会显示稳定的反修饰名称。将符号交给 bpftrace 之前,可以先通过地址解析原始符号: ```console -$ cargo install rustfilt -$ nm helloworld/target/release/helloworld > name.txt -$ rustfilt _ZN10helloworld4main17h2dce92cb81426b91E -helloworld::main -$ rustfilt -i name.txt | grep hello -0000000000008b60 t helloworld::main +$ main_address=$(nm -C target/debug/helloworld | awk '$2 ~ /^[tT]$/ && $3 ~ /^helloworld::main(::h[[:xdigit:]]+)?$/ && !matched { found = $1; matched = 1 } END { if (matched) print found }') +$ main_symbol=$(nm target/debug/helloworld | awk -v address="$main_address" '$1 == address && $2 ~ /^[tT]$/ && !matched { found = $3; matched = 1 } END { if (matched) print found }') +$ echo "$main_symbol" +_RNvCsiXtUZV4ocyZ_10helloworld4main ``` -接下来我们可以尝试使用 bpftrace 跟踪对应的函数: +`-C symbol-mangling-version` rustc 选项可以选择名称修饰方案,但从当前二进制解析可以避免依赖某一种方案。现在可以追踪 `main`: ```console -$ sudo bpftrace -e 'uprobe:helloworld/target/release/helloworld:_ZN10helloworld4main17h2dce92cb81426b91E { printf("Function hello-world called\n"); }' +$ sudo bpftrace -e "uprobe:target/debug/helloworld:${main_symbol} { printf(\"Function hello-world called\n\"); }" Attaching 1 probe... Function hello-world called ``` @@ -101,17 +97,20 @@ fn main() { ```console $ cd args $ cargo build -$ nm target/debug/helloworld | grep hello -0000000000016250 t _ZN10helloworld4main17ha3594bca2af541f6E -0000000000016540 t _ZN10helloworld5hello17h5f3a03dda56661e1E +$ nm -C target/debug/helloworld | grep 'helloworld::hello' +0000000000016f90 t helloworld::hello ``` 注意,在 release 模式(`cargo build --release`)下,只会出现 `main` 函数符号,因为 `hello` 在优化过程中被内联了。 -现在我们可以使用符号来追踪 `hello` 函数。由于 Rust 会对符号名称进行混淆并加入每次编译都会变化的哈希值,我们使用通配符模式来匹配任何版本的 `hello` 函数: +Rust 1.97 默认使用 v0 符号名称修饰。bpftrace 需要原始符号,因此先通过稳定的反修饰名称找到地址,再解析对应的原始符号: ```console -$ sudo bpftrace -e 'uprobe:target/debug/helloworld:_ZN10helloworld5hello* { printf("Function hello called\n"); }' +$ hello_address=$(nm -C target/debug/helloworld | awk '$2 ~ /^[tT]$/ && $3 ~ /^helloworld::hello(::h[[:xdigit:]]+)?$/ && !matched { found = $1; matched = 1 } END { if (matched) print found }') +$ hello_symbol=$(nm target/debug/helloworld | awk -v address="$hello_address" '$1 == address && $2 ~ /^[tT]$/ && !matched { found = $3; matched = 1 } END { if (matched) print found }') +$ echo "$hello_symbol" +_RNvCsiXtUZV4ocyZ_10helloworld5hello +$ sudo bpftrace -e "uprobe:target/debug/helloworld:${hello_symbol} { printf(\"Function hello called\n\"); }" Attaching 1 probe... Function hello called Function hello called @@ -133,10 +132,10 @@ Hello, world! 4 in 5 return value: 9 ``` -我们也可以使用 Uretprobe 来获取返回值。同样,我们使用通配符来匹配符号: +我们也可以使用 Uretprobe 和同一个已解析的符号来获取返回值: ```console -$ sudo bpftrace -e 'uretprobe:target/debug/helloworld:_ZN10helloworld5hello* { printf("Function hello returned: %d\n", retval); }' +$ sudo bpftrace -e "uretprobe:target/debug/helloworld:${hello_symbol} { printf(\"Function hello returned: %d\n\", retval); }" Attaching 1 probe... Function hello returned: 6 Function hello returned: 7 @@ -144,7 +143,7 @@ Function hello returned: 8 Function hello returned: 9 ``` -注意:通配符模式 `_ZN10helloworld5hello*` 可以匹配 `hello` 函数符号,无论 Rust 在编译时添加了什么哈希后缀。如果需要,你可以使用 `nm target/debug/helloworld | grep hello` 来查看确切的符号名称。 +通过反修饰名称解析原始符号的方式同时适用于 Rust 的 legacy 和 v0 名称修饰方案。 ## 参考资料