summaryrefslogtreecommitdiffstats
path: root/library/backtrace
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
commitef24de24a82fe681581cc130f342363c47c0969a (patch)
tree0d494f7e1a38b95c92426f58fe6eaa877303a86c /library/backtrace
parentReleasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz
rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/backtrace')
-rw-r--r--library/backtrace/.github/actions/build-with-patched-std/action.yml48
-rw-r--r--library/backtrace/.github/actions/report-code-size-changes/action.yml111
-rw-r--r--library/backtrace/.github/workflows/check-binary-size.yml172
-rw-r--r--library/backtrace/Cargo.lock221
-rw-r--r--library/backtrace/Cargo.toml9
-rw-r--r--library/backtrace/build.rs31
-rw-r--r--library/backtrace/crates/as-if-std/Cargo.toml4
-rw-r--r--library/backtrace/crates/debuglink/Cargo.toml2
-rw-r--r--library/backtrace/crates/dylib-dep/Cargo.toml2
-rw-r--r--library/backtrace/crates/line-tables-only/Cargo.toml2
-rw-r--r--library/backtrace/crates/line-tables-only/src/lib.rs15
-rw-r--r--library/backtrace/crates/macos_frames_test/Cargo.toml2
-rw-r--r--library/backtrace/crates/without_debuginfo/Cargo.toml2
-rw-r--r--library/backtrace/src/android-api.c4
-rw-r--r--library/backtrace/src/backtrace/dbghelp.rs327
-rw-r--r--library/backtrace/src/backtrace/libunwind.rs13
-rw-r--r--library/backtrace/src/backtrace/mod.rs35
-rw-r--r--library/backtrace/src/dbghelp.rs52
-rw-r--r--library/backtrace/src/lib.rs15
-rw-r--r--library/backtrace/src/print.rs11
-rw-r--r--library/backtrace/src/symbolize/dbghelp.rs115
-rw-r--r--library/backtrace/src/symbolize/gimli.rs45
-rw-r--r--library/backtrace/src/symbolize/gimli/elf.rs2
-rw-r--r--library/backtrace/src/symbolize/gimli/libs_aix.rs74
-rw-r--r--library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs20
-rw-r--r--library/backtrace/src/symbolize/gimli/xcoff.rs186
-rw-r--r--library/backtrace/src/windows.rs52
-rw-r--r--library/backtrace/tests/accuracy/main.rs4
-rw-r--r--library/backtrace/tests/sgx-image-base.rs56
-rw-r--r--library/backtrace/tests/smoke.rs51
30 files changed, 1350 insertions, 333 deletions
diff --git a/library/backtrace/.github/actions/build-with-patched-std/action.yml b/library/backtrace/.github/actions/build-with-patched-std/action.yml
new file mode 100644
index 000000000..5466a2289
--- /dev/null
+++ b/library/backtrace/.github/actions/build-with-patched-std/action.yml
@@ -0,0 +1,48 @@
+# Github composite action to build a single-source-file test binary with an
+# already-checked-out version of Rust's stdlib, that will be patched with a
+# given revision of the backtrace crate.
+
+name: Build with patched std
+description: >
+ Build a binary with a version of std that's had a specific revision of
+ backtrace patched in.
+inputs:
+ backtrace-commit:
+ description: The git commit of backtrace to patch in to std
+ required: true
+ main-rs:
+ description: The (single) source code file to compile
+ required: true
+ rustc-dir:
+ description: The root directory of the rustc repo
+ required: true
+outputs:
+ test-binary-size:
+ description: The size in bytes of the built test binary
+ value: ${{ steps.measure.outputs.test-binary-size }}
+runs:
+ using: composite
+ steps:
+ - shell: bash
+ id: measure
+ env:
+ RUSTC_FLAGS: -Copt-level=3 -Cstrip=symbols
+ # This symlink is made by Build::new() in the bootstrap crate, using a
+ # symlink on Linux and a junction on Windows, so it will exist on both
+ # platforms.
+ RUSTC_BUILD_DIR: build/host
+ working-directory: ${{ inputs.rustc-dir }}
+ run: |
+ rm -rf "$RUSTC_BUILD_DIR/stage0-std"
+
+ (cd library/backtrace && git checkout ${{ inputs.backtrace-commit }})
+ git add library/backtrace
+
+ python3 x.py build library --stage 0
+
+ TEMP_BUILD_OUTPUT=$(mktemp test-binary-XXXXXXXX)
+ "$RUSTC_BUILD_DIR/stage0-sysroot/bin/rustc" $RUSTC_FLAGS "${{ inputs.main-rs }}" -o "$TEMP_BUILD_OUTPUT"
+ BINARY_SIZE=$(stat -c '%s' "$TEMP_BUILD_OUTPUT")
+ rm "$TEMP_BUILD_OUTPUT"
+
+ echo "test-binary-size=$BINARY_SIZE" >> "$GITHUB_OUTPUT"
diff --git a/library/backtrace/.github/actions/report-code-size-changes/action.yml b/library/backtrace/.github/actions/report-code-size-changes/action.yml
new file mode 100644
index 000000000..ede26975a
--- /dev/null
+++ b/library/backtrace/.github/actions/report-code-size-changes/action.yml
@@ -0,0 +1,111 @@
+# Github composite action to report on code size changes across different
+# platforms.
+
+name: Report binary size changes on PR
+description: |
+ Report on code size changes across different platforms resulting from a PR.
+ The only input argument is the path to a directory containing a set of
+ "*.json" files (extension required), each file containing the keys:
+
+ - platform: the platform that the code size change was measured on
+ - reference: the size in bytes of the reference binary (base of PR)
+ - updated: the size in bytes of the updated binary (head of PR)
+
+ The size is reported as a comment on the PR (accessed via context).
+inputs:
+ data-directory:
+ description: >
+ Path to directory containing size data as a set of "*.json" files.
+ required: true
+runs:
+ using: composite
+ steps:
+ - name: Post a PR comment if the size has changed
+ uses: actions/github-script@v6
+ env:
+ DATA_DIRECTORY: ${{ inputs.data-directory }}
+ with:
+ script: |
+ const fs = require("fs");
+
+ const size_dir = process.env.DATA_DIRECTORY;
+
+ // Map the set of all the *.json files into an array of objects.
+ const globber = await glob.create(`${size_dir}/*.json`);
+ const files = await globber.glob();
+ const sizes = files.map(path => {
+ const contents = fs.readFileSync(path);
+ return JSON.parse(contents);
+ });
+
+ // Map each object into some text, but only if it shows any difference
+ // to report.
+ const size_reports = sizes.flatMap(size_data => {
+ const platform = size_data["platform"];
+ const reference = size_data["reference"];
+ const updated = size_data["updated"];
+
+ if (!(reference > 0)) {
+ core.setFailed(`Reference size invalid: ${reference}`);
+ return;
+ }
+
+ if (!(updated > 0)) {
+ core.setFailed(`Updated size invalid: ${updated}`);
+ return;
+ }
+
+ const formatter = Intl.NumberFormat("en", {
+ useGrouping: "always"
+ });
+
+ const updated_str = formatter.format(updated);
+ const reference_str = formatter.format(reference);
+
+ const diff = updated - reference;
+ const diff_pct = (updated / reference) - 1;
+
+ const diff_str = Intl.NumberFormat("en", {
+ useGrouping: "always",
+ sign: "exceptZero"
+ }).format(diff);
+
+ const diff_pct_str = Intl.NumberFormat("en", {
+ style: "percent",
+ useGrouping: "always",
+ sign: "exceptZero",
+ maximumFractionDigits: 2
+ }).format(diff_pct);
+
+ if (diff !== 0) {
+ // The body is created here and wrapped so "weirdly" to avoid whitespace at the start of the lines,
+ // which is interpreted as a code block by Markdown.
+ const report = `On platform \`${platform}\`:
+
+ - Original binary size: **${reference_str} B**
+ - Updated binary size: **${updated_str} B**
+ - Difference: **${diff_str} B** (${diff_pct_str})
+
+ `;
+
+ return [report];
+ } else {
+ return [];
+ }
+ });
+
+ // If there are any size changes to report, format a comment and post
+ // it.
+ if (size_reports.length > 0) {
+ const comment_sizes = size_reports.join("");
+ const body = `Code size changes for a hello-world Rust program linked with libstd with backtrace:
+
+ ${comment_sizes}`;
+
+ github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body
+ });
+ }
diff --git a/library/backtrace/.github/workflows/check-binary-size.yml b/library/backtrace/.github/workflows/check-binary-size.yml
index 0beae1da9..d045fb7b3 100644
--- a/library/backtrace/.github/workflows/check-binary-size.yml
+++ b/library/backtrace/.github/workflows/check-binary-size.yml
@@ -9,75 +9,143 @@ on:
branches:
- master
+# Both the "measure" and "report" jobs need to know this.
+env:
+ SIZE_DATA_DIR: sizes
+
+# Responsibility is divided between two jobs "measure" and "report", so that the
+# job that builds (and potentnially runs) untrusted code does not have PR write
+# permission, and vice-versa.
jobs:
- test:
+ measure:
name: Check binary size
- runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ platform: [ubuntu-latest, windows-latest]
+ runs-on: ${{ matrix.platform }}
permissions:
- pull-requests: write
+ contents: read
+ env:
+ # This cannot be used as a context variable in the 'uses' key later. If it
+ # changes, update those steps too.
+ BACKTRACE_DIR: backtrace
+ RUSTC_DIR: rustc
+ TEST_MAIN_RS: foo.rs
+ BASE_COMMIT: ${{ github.event.pull_request.base.sha }}
+ HEAD_COMMIT: ${{ github.event.pull_request.head.sha }}
+ SIZE_DATA_FILE: size-${{ strategy.job-index }}.json
steps:
- name: Print info
+ shell: bash
run: |
- echo "Current SHA: ${{ github.event.pull_request.head.sha }}"
- echo "Base SHA: ${{ github.event.pull_request.base.sha }}"
+ echo "Current SHA: $HEAD_COMMIT"
+ echo "Base SHA: $BASE_COMMIT"
+ # Note: the backtrace source that's cloned here is NOT the version to be
+ # patched in to std. It's cloned here to access the Github action for
+ # building and measuring the test binary.
+ - name: Clone backtrace to access Github action
+ uses: actions/checkout@v3
+ with:
+ path: ${{ env.BACKTRACE_DIR }}
- name: Clone Rustc
uses: actions/checkout@v3
with:
repository: rust-lang/rust
- fetch-depth: 1
- - name: Fetch backtrace
- run: git submodule update --init library/backtrace
- - name: Create hello world program that uses backtrace
- run: printf "fn main() { panic!(); }" > foo.rs
- - name: Build binary with base version of backtrace
+ path: ${{ env.RUSTC_DIR }}
+ - name: Set up std repository and backtrace submodule for size test
+ shell: bash
+ working-directory: ${{ env.RUSTC_DIR }}
+ env:
+ PR_SOURCE_REPO: ${{ github.event.pull_request.head.repo.full_name }}
run: |
- printf "[llvm]\ndownload-ci-llvm = true\n\n[rust]\nincremental = false\n" > config.toml
+ # Bootstrap config
+ cat <<EOF > config.toml
+ [llvm]
+ download-ci-llvm = true
+ [rust]
+ incremental = false
+ EOF
+
+ # Test program source
+ cat <<EOF > $TEST_MAIN_RS
+ fn main() {
+ panic!();
+ }
+ EOF
+
+ git submodule update --init library/backtrace
+
cd library/backtrace
- git remote add head-pr https://github.com/${{ github.event.pull_request.head.repo.full_name }}
+ git remote add head-pr "https://github.com/$PR_SOURCE_REPO"
git fetch --all
- git checkout ${{ github.event.pull_request.base.sha }}
- cd ../..
- git add library/backtrace
- python3 x.py build library --stage 0
- ./build/x86_64-unknown-linux-gnu/stage0-sysroot/bin/rustc -O foo.rs -o binary-reference
+ - name: Build binary with base version of backtrace
+ uses: ./backtrace/.github/actions/build-with-patched-std
+ with:
+ backtrace-commit: ${{ env.BASE_COMMIT }}
+ main-rs: ${{ env.TEST_MAIN_RS }}
+ rustc-dir: ${{ env.RUSTC_DIR }}
+ id: size-reference
- name: Build binary with PR version of backtrace
- run: |
- cd library/backtrace
- git checkout ${{ github.event.pull_request.head.sha }}
- cd ../..
- git add library/backtrace
- rm -rf build/x86_64-unknown-linux-gnu/stage0-std
- python3 x.py build library --stage 0
- ./build/x86_64-unknown-linux-gnu/stage0-sysroot/bin/rustc -O foo.rs -o binary-updated
- - name: Display binary size
- run: |
- ls -la binary-*
- echo "SIZE_REFERENCE=$(stat -c '%s' binary-reference)" >> "$GITHUB_ENV"
- echo "SIZE_UPDATED=$(stat -c '%s' binary-updated)" >> "$GITHUB_ENV"
- - name: Post a PR comment if the size has changed
+ uses: ./backtrace/.github/actions/build-with-patched-std
+ with:
+ backtrace-commit: ${{ env.HEAD_COMMIT }}
+ main-rs: ${{ env.TEST_MAIN_RS }}
+ rustc-dir: ${{ env.RUSTC_DIR }}
+ id: size-updated
+ # There is no built-in way to "collect" all the outputs of a set of jobs
+ # run with a matrix strategy. Subsequent jobs that have a "needs"
+ # dependency on this one will be run once, when the last matrix job is
+ # run. Appending data to a single file within a matrix is subject to race
+ # conditions. So we write the size data to files with distinct names
+ # generated from the job index.
+ - name: Write sizes to file
uses: actions/github-script@v6
+ env:
+ SIZE_REFERENCE: ${{ steps.size-reference.outputs.test-binary-size }}
+ SIZE_UPDATED: ${{ steps.size-updated.outputs.test-binary-size }}
+ PLATFORM: ${{ matrix.platform }}
with:
script: |
- const reference = process.env.SIZE_REFERENCE;
- const updated = process.env.SIZE_UPDATED;
- const diff = updated - reference;
- const plus = diff > 0 ? "+" : "";
- const diff_str = `${plus}${diff}B`;
+ const fs = require("fs");
+ const path = require("path");
- if (diff !== 0) {
- const percent = (((updated / reference) - 1) * 100).toFixed(2);
- // The body is created here and wrapped so "weirdly" to avoid whitespace at the start of the lines,
- // which is interpreted as a code block by Markdown.
- const body = `Below is the size of a hello-world Rust program linked with libstd with backtrace.
+ fs.mkdirSync(process.env.SIZE_DATA_DIR, {recursive: true});
- Original binary size: **${reference}B**
- Updated binary size: **${updated}B**
- Difference: **${diff_str}** (${percent}%)`;
+ const output_data = JSON.stringify({
+ platform: process.env.PLATFORM,
+ reference: process.env.SIZE_REFERENCE,
+ updated: process.env.SIZE_UPDATED,
+ });
- github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body
- })
- }
+ // The "wx" flag makes this fail if the file exists, which we want,
+ // because there should be no collisions.
+ fs.writeFileSync(
+ path.join(process.env.SIZE_DATA_DIR, process.env.SIZE_DATA_FILE),
+ output_data,
+ { flag: "wx" },
+ );
+ - name: Upload size data
+ uses: actions/upload-artifact@v3
+ with:
+ name: size-files
+ path: ${{ env.SIZE_DATA_DIR }}/${{ env.SIZE_DATA_FILE }}
+ retention-days: 1
+ if-no-files-found: error
+ report:
+ name: Report binary size changes
+ runs-on: ubuntu-latest
+ needs: measure
+ permissions:
+ pull-requests: write
+ steps:
+ # Clone backtrace to access Github composite actions to report size.
+ - uses: actions/checkout@v3
+ - name: Download size data
+ uses: actions/download-artifact@v3
+ with:
+ name: size-files
+ path: ${{ env.SIZE_DATA_DIR }}
+ - name: Analyze and report size changes
+ uses: ./.github/actions/report-code-size-changes
+ with:
+ data-directory: ${{ env.SIZE_DATA_DIR }}
diff --git a/library/backtrace/Cargo.lock b/library/backtrace/Cargo.lock
new file mode 100644
index 000000000..619a1392f
--- /dev/null
+++ b/library/backtrace/Cargo.lock
@@ -0,0 +1,221 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "as-if-std"
+version = "0.1.0"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "cpp_demangle",
+ "dylib-dep",
+ "libc",
+ "libloading",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "rustc-serialize",
+ "serde",
+ "winapi",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cpp_demangle"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "cpp_smoke_test"
+version = "0.1.0"
+dependencies = [
+ "backtrace",
+ "cc",
+]
+
+[[package]]
+name = "dylib-dep"
+version = "0.1.0"
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "object"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc-serialize"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/library/backtrace/Cargo.toml b/library/backtrace/Cargo.toml
index 6714b3b7d..932310bcf 100644
--- a/library/backtrace/Cargo.toml
+++ b/library/backtrace/Cargo.toml
@@ -13,8 +13,9 @@ A library to acquire a stack trace (backtrace) at runtime in a Rust program.
"""
autoexamples = true
autotests = true
-edition = "2018"
+edition = "2021"
exclude = ["/ci/"]
+rust-version = "1.65.0"
[workspace]
members = ['crates/cpp_smoke_test', 'crates/as-if-std']
@@ -45,7 +46,7 @@ libc = { version = "0.2.146", default-features = false }
[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies.object]
version = "0.32.0"
default-features = false
-features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive']
+features = ['read_core', 'elf', 'macho', 'pe', 'xcoff', 'unaligned', 'archive']
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", optional = true }
@@ -118,12 +119,12 @@ required-features = ["std"]
[[test]]
name = "smoke"
required-features = ["std"]
-edition = '2018'
+edition = '2021'
[[test]]
name = "accuracy"
required-features = ["std"]
-edition = '2018'
+edition = '2021'
[[test]]
name = "concurrent-panics"
diff --git a/library/backtrace/build.rs b/library/backtrace/build.rs
index 9bd3abd16..ed4e07a85 100644
--- a/library/backtrace/build.rs
+++ b/library/backtrace/build.rs
@@ -11,17 +11,27 @@ pub fn main() {
}
}
+// Used to detect the value of the `__ANDROID_API__`
+// builtin #define
+const MARKER: &str = "BACKTRACE_RS_ANDROID_APIVERSION";
+const ANDROID_API_C: &str = "
+BACKTRACE_RS_ANDROID_APIVERSION __ANDROID_API__
+";
+
fn build_android() {
- // Resolve `src/android-api.c` relative to this file.
+ // Create `android-api.c` on demand.
// Required to support calling this from the `std` build script.
- let android_api_c = Path::new(file!())
- .parent()
- .unwrap()
- .join("src/android-api.c");
- let expansion = match cc::Build::new().file(android_api_c).try_expand() {
+ let out_dir = env::var_os("OUT_DIR").unwrap();
+ let android_api_c = Path::new(&out_dir).join("android-api.c");
+ std::fs::write(&android_api_c, ANDROID_API_C).unwrap();
+
+ let expansion = match cc::Build::new().file(&android_api_c).try_expand() {
Ok(result) => result,
Err(e) => {
- println!("failed to run C compiler: {}", e);
+ eprintln!(
+ "warning: android version detection failed while running C compiler: {}",
+ e
+ );
return;
}
};
@@ -29,13 +39,12 @@ fn build_android() {
Ok(s) => s,
Err(_) => return,
};
- println!("expanded android version detection:\n{}", expansion);
- let marker = "APIVERSION";
- let i = match expansion.find(marker) {
+ eprintln!("expanded android version detection:\n{}", expansion);
+ let i = match expansion.find(MARKER) {
Some(i) => i,
None => return,
};
- let version = match expansion[i + marker.len() + 1..].split_whitespace().next() {
+ let version = match expansion[i + MARKER.len() + 1..].split_whitespace().next() {
Some(s) => s,
None => return,
};
diff --git a/library/backtrace/crates/as-if-std/Cargo.toml b/library/backtrace/crates/as-if-std/Cargo.toml
index bcbcfe159..7f12cfb56 100644
--- a/library/backtrace/crates/as-if-std/Cargo.toml
+++ b/library/backtrace/crates/as-if-std/Cargo.toml
@@ -2,7 +2,7 @@
name = "as-if-std"
version = "0.1.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
-edition = "2018"
+edition = "2021"
publish = false
[lib]
@@ -24,7 +24,7 @@ addr2line = { version = "0.21.0", optional = true, default-features = false }
version = "0.32.0"
default-features = false
optional = true
-features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive']
+features = ['read_core', 'elf', 'macho', 'pe', 'xcoff', 'unaligned', 'archive']
[build-dependencies]
# Dependency of the `backtrace` crate
diff --git a/library/backtrace/crates/debuglink/Cargo.toml b/library/backtrace/crates/debuglink/Cargo.toml
index 6b55b1394..5e62abd37 100644
--- a/library/backtrace/crates/debuglink/Cargo.toml
+++ b/library/backtrace/crates/debuglink/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "debuglink"
version = "0.1.0"
-edition = "2018"
+edition = "2021"
[dependencies]
backtrace = { path = "../.." }
diff --git a/library/backtrace/crates/dylib-dep/Cargo.toml b/library/backtrace/crates/dylib-dep/Cargo.toml
index c3d4a8c2f..e6cc9c23b 100644
--- a/library/backtrace/crates/dylib-dep/Cargo.toml
+++ b/library/backtrace/crates/dylib-dep/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "dylib-dep"
version = "0.1.0"
-edition = "2018"
+edition = "2021"
authors = []
publish = false
diff --git a/library/backtrace/crates/line-tables-only/Cargo.toml b/library/backtrace/crates/line-tables-only/Cargo.toml
index e2967d3d3..8d17db58c 100644
--- a/library/backtrace/crates/line-tables-only/Cargo.toml
+++ b/library/backtrace/crates/line-tables-only/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "line-tables-only"
version = "0.1.0"
-edition = "2018"
+edition = "2021"
[build-dependencies]
cc = "1.0"
diff --git a/library/backtrace/crates/line-tables-only/src/lib.rs b/library/backtrace/crates/line-tables-only/src/lib.rs
index bd5afcb3a..b292b8441 100644
--- a/library/backtrace/crates/line-tables-only/src/lib.rs
+++ b/library/backtrace/crates/line-tables-only/src/lib.rs
@@ -1,8 +1,8 @@
#[cfg(test)]
mod tests {
- use std::path::Path;
use backtrace::Backtrace;
use libc::c_void;
+ use std::path::Path;
pub type Callback = extern "C" fn(data: *mut c_void);
@@ -15,11 +15,12 @@ mod tests {
unsafe { *(data as *mut Option<Backtrace>) = Some(bt) };
}
- fn assert_contains(backtrace: &Backtrace,
- expected_name: &str,
- expected_file: &str,
- expected_line: u32) {
-
+ fn assert_contains(
+ backtrace: &Backtrace,
+ expected_name: &str,
+ expected_file: &str,
+ expected_line: u32,
+ ) {
let expected_file = Path::new(expected_file);
for frame in backtrace.frames() {
@@ -34,7 +35,7 @@ mod tests {
}
}
- panic!("symbol {:?} not found in backtrace: {:?}", expected_name, backtrace);
+ panic!("symbol {expected_name:?} not found in backtrace: {backtrace:?}");
}
/// Verifies that when debug info includes only lines tables the generated
diff --git a/library/backtrace/crates/macos_frames_test/Cargo.toml b/library/backtrace/crates/macos_frames_test/Cargo.toml
index 278d51e79..849e76414 100644
--- a/library/backtrace/crates/macos_frames_test/Cargo.toml
+++ b/library/backtrace/crates/macos_frames_test/Cargo.toml
@@ -2,7 +2,7 @@
name = "macos_frames_test"
version = "0.1.0"
authors = ["Aaron Hill <aa1ronham@gmail.com>"]
-edition = "2018"
+edition = "2021"
[dependencies.backtrace]
path = "../.."
diff --git a/library/backtrace/crates/without_debuginfo/Cargo.toml b/library/backtrace/crates/without_debuginfo/Cargo.toml
index 19d76cbec..38e559971 100644
--- a/library/backtrace/crates/without_debuginfo/Cargo.toml
+++ b/library/backtrace/crates/without_debuginfo/Cargo.toml
@@ -2,7 +2,7 @@
name = "without_debuginfo"
version = "0.1.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
-edition = "2018"
+edition = "2021"
[dependencies.backtrace]
path = "../.."
diff --git a/library/backtrace/src/android-api.c b/library/backtrace/src/android-api.c
deleted file mode 100644
index 1bfeadf5b..000000000
--- a/library/backtrace/src/android-api.c
+++ /dev/null
@@ -1,4 +0,0 @@
-// Used from the build script to detect the value of the `__ANDROID_API__`
-// builtin #define
-
-APIVERSION __ANDROID_API__
diff --git a/library/backtrace/src/backtrace/dbghelp.rs b/library/backtrace/src/backtrace/dbghelp.rs
index ba0f05f3b..d1b76e281 100644
--- a/library/backtrace/src/backtrace/dbghelp.rs
+++ b/library/backtrace/src/backtrace/dbghelp.rs
@@ -1,30 +1,32 @@
//! Backtrace strategy for MSVC platforms.
//!
-//! This module contains the ability to generate a backtrace on MSVC using one
-//! of two possible methods. The `StackWalkEx` function is primarily used if
-//! possible, but not all systems have that. Failing that the `StackWalk64`
-//! function is used instead. Note that `StackWalkEx` is favored because it
-//! handles debuginfo internally and returns inline frame information.
+//! This module contains the ability to capture a backtrace on MSVC using one
+//! of three possible methods. For `x86_64` and `aarch64`, we use `RtlVirtualUnwind`
+//! to walk the stack one frame at a time. This function is much faster than using
+//! `dbghelp!StackWalk*` because it does not load debug info to report inlined frames.
+//! We still report inlined frames during symbolization by consulting the appropriate
+//! `dbghelp` functions.
+//!
+//! For all other platforms, primarily `i686`, the `StackWalkEx` function is used if
+//! possible, but not all systems have that. Failing that the `StackWalk64` function
+//! is used instead. Note that `StackWalkEx` is favored because it handles debuginfo
+//! internally and returns inline frame information.
//!
//! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs`
//! for more information about that.
#![allow(bad_style)]
-use super::super::{dbghelp, windows::*};
+use super::super::windows::*;
use core::ffi::c_void;
-use core::mem;
-
-#[derive(Clone, Copy)]
-pub enum StackFrame {
- New(STACKFRAME_EX),
- Old(STACKFRAME64),
-}
#[derive(Clone, Copy)]
pub struct Frame {
- pub(crate) stack_frame: StackFrame,
base_address: *mut c_void,
+ ip: *mut c_void,
+ sp: *mut c_void,
+ #[cfg(not(target_env = "gnu"))]
+ inline_context: Option<DWORD>,
}
// we're just sending around raw pointers and reading them, never interpreting
@@ -34,62 +36,144 @@ unsafe impl Sync for Frame {}
impl Frame {
pub fn ip(&self) -> *mut c_void {
- self.addr_pc().Offset as *mut _
+ self.ip
}
pub fn sp(&self) -> *mut c_void {
- self.addr_stack().Offset as *mut _
+ self.sp
}
pub fn symbol_address(&self) -> *mut c_void {
- self.ip()
+ self.ip
}
pub fn module_base_address(&self) -> Option<*mut c_void> {
Some(self.base_address)
}
- fn addr_pc(&self) -> &ADDRESS64 {
- match self.stack_frame {
- StackFrame::New(ref new) => &new.AddrPC,
- StackFrame::Old(ref old) => &old.AddrPC,
- }
+ #[cfg(not(target_env = "gnu"))]
+ pub fn inline_context(&self) -> Option<DWORD> {
+ self.inline_context
}
+}
- fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
- match self.stack_frame {
- StackFrame::New(ref mut new) => &mut new.AddrPC,
- StackFrame::Old(ref mut old) => &mut old.AddrPC,
- }
+#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
+struct MyContext(CONTEXT);
+
+#[cfg(target_arch = "x86_64")]
+impl MyContext {
+ #[inline(always)]
+ fn ip(&self) -> DWORD64 {
+ self.0.Rip
}
- fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
- match self.stack_frame {
- StackFrame::New(ref mut new) => &mut new.AddrFrame,
- StackFrame::Old(ref mut old) => &mut old.AddrFrame,
- }
+ #[inline(always)]
+ fn sp(&self) -> DWORD64 {
+ self.0.Rsp
}
+}
- fn addr_stack(&self) -> &ADDRESS64 {
- match self.stack_frame {
- StackFrame::New(ref new) => &new.AddrStack,
- StackFrame::Old(ref old) => &old.AddrStack,
- }
+#[cfg(target_arch = "aarch64")]
+impl MyContext {
+ #[inline(always)]
+ fn ip(&self) -> DWORD64 {
+ self.0.Pc
}
- fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
- match self.stack_frame {
- StackFrame::New(ref mut new) => &mut new.AddrStack,
- StackFrame::Old(ref mut old) => &mut old.AddrStack,
- }
+ #[inline(always)]
+ fn sp(&self) -> DWORD64 {
+ self.0.Sp
}
}
-#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
-struct MyContext(CONTEXT);
+#[cfg(target_arch = "x86")]
+impl MyContext {
+ #[inline(always)]
+ fn ip(&self) -> DWORD {
+ self.0.Eip
+ }
+
+ #[inline(always)]
+ fn sp(&self) -> DWORD {
+ self.0.Esp
+ }
+ #[inline(always)]
+ fn fp(&self) -> DWORD {
+ self.0.Ebp
+ }
+}
+
+#[cfg(target_arch = "arm")]
+impl MyContext {
+ #[inline(always)]
+ fn ip(&self) -> DWORD {
+ self.0.Pc
+ }
+
+ #[inline(always)]
+ fn sp(&self) -> DWORD {
+ self.0.Sp
+ }
+
+ #[inline(always)]
+ fn fp(&self) -> DWORD {
+ self.0.R11
+ }
+}
+
+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
#[inline(always)]
pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
+ use core::ptr;
+
+ let mut context = core::mem::zeroed::<MyContext>();
+ RtlCaptureContext(&mut context.0);
+
+ // Call `RtlVirtualUnwind` to find the previous stack frame, walking until we hit ip = 0.
+ while context.ip() != 0 {
+ let mut base = 0;
+
+ let fn_entry = RtlLookupFunctionEntry(context.ip(), &mut base, ptr::null_mut());
+ if fn_entry.is_null() {
+ break;
+ }
+
+ let frame = super::Frame {
+ inner: Frame {
+ base_address: fn_entry as *mut c_void,
+ ip: context.ip() as *mut c_void,
+ sp: context.sp() as *mut c_void,
+ #[cfg(not(target_env = "gnu"))]
+ inline_context: None,
+ },
+ };
+
+ if !cb(&frame) {
+ break;
+ }
+
+ let mut handler_data = 0usize;
+ let mut establisher_frame = 0;
+
+ RtlVirtualUnwind(
+ 0,
+ base,
+ context.ip(),
+ fn_entry,
+ &mut context.0,
+ &mut handler_data as *mut usize as *mut PVOID,
+ &mut establisher_frame,
+ ptr::null_mut(),
+ );
+ }
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "arm"))]
+#[inline(always)]
+pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
+ use core::mem;
+
// Allocate necessary structures for doing the stack walk
let process = GetCurrentProcess();
let thread = GetCurrentThread();
@@ -98,65 +182,40 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
RtlCaptureContext(&mut context.0);
// Ensure this process's symbols are initialized
- let dbghelp = match dbghelp::init() {
+ let dbghelp = match super::super::dbghelp::init() {
Ok(dbghelp) => dbghelp,
Err(()) => return, // oh well...
};
- // On x86_64 and ARM64 we opt to not use the default `Sym*` functions from
- // dbghelp for getting the function table and module base. Instead we use
- // the `RtlLookupFunctionEntry` function in kernel32 which will account for
- // JIT compiler frames as well. These should be equivalent, but using
- // `Rtl*` allows us to backtrace through JIT frames.
- //
- // Note that `RtlLookupFunctionEntry` only works for in-process backtraces,
- // but that's all we support anyway, so it all lines up well.
- cfg_if::cfg_if! {
- if #[cfg(target_pointer_width = "64")] {
- use core::ptr;
-
- unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID {
- let mut base = 0;
- RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast()
- }
-
- unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 {
- let mut base = 0;
- RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut());
- base
- }
- } else {
- let function_table_access = dbghelp.SymFunctionTableAccess64();
- let get_module_base = dbghelp.SymGetModuleBase64();
- }
- }
+ let function_table_access = dbghelp.SymFunctionTableAccess64();
+ let get_module_base = dbghelp.SymGetModuleBase64();
let process_handle = GetCurrentProcess();
+ #[cfg(target_arch = "x86")]
+ let image = IMAGE_FILE_MACHINE_I386;
+ #[cfg(target_arch = "arm")]
+ let image = IMAGE_FILE_MACHINE_ARMNT;
+
// Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
// since it's in theory supported on more systems.
match (*dbghelp.dbghelp()).StackWalkEx() {
Some(StackWalkEx) => {
- let mut inner: STACKFRAME_EX = mem::zeroed();
- inner.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as DWORD;
- let mut frame = super::Frame {
- inner: Frame {
- stack_frame: StackFrame::New(inner),
- base_address: 0 as _,
- },
- };
- let image = init_frame(&mut frame.inner, &context.0);
- let frame_ptr = match &mut frame.inner.stack_frame {
- StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX,
- _ => unreachable!(),
- };
+ let mut stack_frame_ex: STACKFRAME_EX = mem::zeroed();
+ stack_frame_ex.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as DWORD;
+ stack_frame_ex.AddrPC.Offset = context.ip() as u64;
+ stack_frame_ex.AddrPC.Mode = AddrModeFlat;
+ stack_frame_ex.AddrStack.Offset = context.sp() as u64;
+ stack_frame_ex.AddrStack.Mode = AddrModeFlat;
+ stack_frame_ex.AddrFrame.Offset = context.fp() as u64;
+ stack_frame_ex.AddrFrame.Mode = AddrModeFlat;
while StackWalkEx(
image as DWORD,
process,
thread,
- frame_ptr,
- &mut context.0 as *mut CONTEXT as *mut _,
+ &mut stack_frame_ex,
+ &mut context.0 as *mut CONTEXT as PVOID,
None,
Some(function_table_access),
Some(get_module_base),
@@ -164,7 +223,16 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
0,
) == TRUE
{
- frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
+ let frame = super::Frame {
+ inner: Frame {
+ base_address: get_module_base(process_handle, stack_frame_ex.AddrPC.Offset)
+ as *mut c_void,
+ ip: stack_frame_ex.AddrPC.Offset as *mut c_void,
+ sp: stack_frame_ex.AddrStack.Offset as *mut c_void,
+ #[cfg(not(target_env = "gnu"))]
+ inline_context: Some(stack_frame_ex.InlineFrameContext),
+ },
+ };
if !cb(&frame) {
break;
@@ -172,31 +240,36 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
}
}
None => {
- let mut frame = super::Frame {
- inner: Frame {
- stack_frame: StackFrame::Old(mem::zeroed()),
- base_address: 0 as _,
- },
- };
- let image = init_frame(&mut frame.inner, &context.0);
- let frame_ptr = match &mut frame.inner.stack_frame {
- StackFrame::Old(ptr) => ptr as *mut STACKFRAME64,
- _ => unreachable!(),
- };
+ let mut stack_frame64: STACKFRAME64 = mem::zeroed();
+ stack_frame64.AddrPC.Offset = context.ip() as u64;
+ stack_frame64.AddrPC.Mode = AddrModeFlat;
+ stack_frame64.AddrStack.Offset = context.sp() as u64;
+ stack_frame64.AddrStack.Mode = AddrModeFlat;
+ stack_frame64.AddrFrame.Offset = context.fp() as u64;
+ stack_frame64.AddrFrame.Mode = AddrModeFlat;
while dbghelp.StackWalk64()(
image as DWORD,
process,
thread,
- frame_ptr,
- &mut context.0 as *mut CONTEXT as *mut _,
+ &mut stack_frame64,
+ &mut context.0 as *mut CONTEXT as PVOID,
None,
Some(function_table_access),
Some(get_module_base),
None,
) == TRUE
{
- frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
+ let frame = super::Frame {
+ inner: Frame {
+ base_address: get_module_base(process_handle, stack_frame64.AddrPC.Offset)
+ as *mut c_void,
+ ip: stack_frame64.AddrPC.Offset as *mut c_void,
+ sp: stack_frame64.AddrStack.Offset as *mut c_void,
+ #[cfg(not(target_env = "gnu"))]
+ inline_context: None,
+ },
+ };
if !cb(&frame) {
break;
@@ -205,53 +278,3 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
}
}
}
-
-#[cfg(target_arch = "x86_64")]
-fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
- frame.addr_pc_mut().Offset = ctx.Rip as u64;
- frame.addr_pc_mut().Mode = AddrModeFlat;
- frame.addr_stack_mut().Offset = ctx.Rsp as u64;
- frame.addr_stack_mut().Mode = AddrModeFlat;
- frame.addr_frame_mut().Offset = ctx.Rbp as u64;
- frame.addr_frame_mut().Mode = AddrModeFlat;
-
- IMAGE_FILE_MACHINE_AMD64
-}
-
-#[cfg(target_arch = "x86")]
-fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
- frame.addr_pc_mut().Offset = ctx.Eip as u64;
- frame.addr_pc_mut().Mode = AddrModeFlat;
- frame.addr_stack_mut().Offset = ctx.Esp as u64;
- frame.addr_stack_mut().Mode = AddrModeFlat;
- frame.addr_frame_mut().Offset = ctx.Ebp as u64;
- frame.addr_frame_mut().Mode = AddrModeFlat;
-
- IMAGE_FILE_MACHINE_I386
-}
-
-#[cfg(target_arch = "aarch64")]
-fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
- frame.addr_pc_mut().Offset = ctx.Pc as u64;
- frame.addr_pc_mut().Mode = AddrModeFlat;
- frame.addr_stack_mut().Offset = ctx.Sp as u64;
- frame.addr_stack_mut().Mode = AddrModeFlat;
- unsafe {
- frame.addr_frame_mut().Offset = ctx.u.s().Fp as u64;
- }
- frame.addr_frame_mut().Mode = AddrModeFlat;
- IMAGE_FILE_MACHINE_ARM64
-}
-
-#[cfg(target_arch = "arm")]
-fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
- frame.addr_pc_mut().Offset = ctx.Pc as u64;
- frame.addr_pc_mut().Mode = AddrModeFlat;
- frame.addr_stack_mut().Offset = ctx.Sp as u64;
- frame.addr_stack_mut().Mode = AddrModeFlat;
- unsafe {
- frame.addr_frame_mut().Offset = ctx.R11 as u64;
- }
- frame.addr_frame_mut().Mode = AddrModeFlat;
- IMAGE_FILE_MACHINE_ARMNT
-}
diff --git a/library/backtrace/src/backtrace/libunwind.rs b/library/backtrace/src/backtrace/libunwind.rs
index aefa8b094..0cf6365f7 100644
--- a/library/backtrace/src/backtrace/libunwind.rs
+++ b/library/backtrace/src/backtrace/libunwind.rs
@@ -40,7 +40,18 @@ impl Frame {
Frame::Raw(ctx) => ctx,
Frame::Cloned { ip, .. } => return ip,
};
- unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
+ #[allow(unused_mut)]
+ let mut ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void };
+
+ // To reduce TCB size in SGX enclaves, we do not want to implement
+ // symbol resolution functionality. Rather, we can print the offset of
+ // the address here, which could be later mapped to correct function.
+ #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))]
+ {
+ let image_base = super::get_image_base();
+ ip = usize::wrapping_sub(ip as usize, image_base as _) as _;
+ }
+ ip
}
pub fn sp(&self) -> *mut c_void {
diff --git a/library/backtrace/src/backtrace/mod.rs b/library/backtrace/src/backtrace/mod.rs
index 6ca1080c4..1b812d84e 100644
--- a/library/backtrace/src/backtrace/mod.rs
+++ b/library/backtrace/src/backtrace/mod.rs
@@ -125,6 +125,39 @@ impl fmt::Debug for Frame {
}
}
+#[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))]
+mod sgx_no_std_image_base {
+ use core::ffi::c_void;
+ use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+
+ static IMAGE_BASE: AtomicUsize = AtomicUsize::new(0);
+
+ /// Set the image base address. This is only available for Fortanix SGX
+ /// target when the `std` feature is not enabled. This can be used in the
+ /// standard library to set the correct base address.
+ #[doc(hidden)]
+ pub fn set_image_base(base_addr: *mut c_void) {
+ IMAGE_BASE.store(base_addr as _, SeqCst);
+ }
+
+ pub(crate) fn get_image_base() -> *mut c_void {
+ IMAGE_BASE.load(SeqCst) as _
+ }
+}
+
+#[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))]
+pub use self::sgx_no_std_image_base::set_image_base;
+
+#[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))]
+#[deny(unused)]
+pub(crate) use self::sgx_no_std_image_base::get_image_base;
+
+#[cfg(all(target_env = "sgx", target_vendor = "fortanix", feature = "std"))]
+#[deny(unused)]
+pub(crate) fn get_image_base() -> *mut c_void {
+ std::os::fortanix_sgx::mem::image_base() as _
+}
+
cfg_if::cfg_if! {
// This needs to come first, to ensure that
// Miri takes priority over the host platform
@@ -153,8 +186,6 @@ cfg_if::cfg_if! {
mod dbghelp;
use self::dbghelp::trace as trace_imp;
pub(crate) use self::dbghelp::Frame as FrameImp;
- #[cfg(target_env = "msvc")] // only used in dbghelp symbolize
- pub(crate) use self::dbghelp::StackFrame;
} else {
mod noop;
use self::noop::trace as trace_imp;
diff --git a/library/backtrace/src/dbghelp.rs b/library/backtrace/src/dbghelp.rs
index c81766bae..e456dd45d 100644
--- a/library/backtrace/src/dbghelp.rs
+++ b/library/backtrace/src/dbghelp.rs
@@ -34,8 +34,8 @@ use core::ptr;
mod dbghelp {
use crate::windows::*;
pub use winapi::um::dbghelp::{
- StackWalk64, StackWalkEx, SymCleanup, SymFromAddrW, SymFunctionTableAccess64,
- SymGetLineFromAddrW64, SymGetModuleBase64, SymGetOptions, SymInitializeW, SymSetOptions,
+ StackWalk64, StackWalkEx, SymFromAddrW, SymFunctionTableAccess64, SymGetLineFromAddrW64,
+ SymGetModuleBase64, SymGetOptions, SymInitializeW, SymSetOptions,
};
extern "system" {
@@ -55,6 +55,16 @@ mod dbghelp {
pdwDisplacement: PDWORD,
Line: PIMAGEHLP_LINEW64,
) -> BOOL;
+ pub fn SymAddrIncludeInlineTrace(hProcess: HANDLE, Address: DWORD64) -> DWORD;
+ pub fn SymQueryInlineTrace(
+ hProcess: HANDLE,
+ StartAddress: DWORD64,
+ StartContext: DWORD,
+ StartRetAddress: DWORD64,
+ CurAddress: DWORD64,
+ CurContext: LPDWORD,
+ CurFrameIndex: LPDWORD,
+ ) -> BOOL;
}
pub fn assert_equal_types<T>(a: T, _b: T) -> T {
@@ -164,7 +174,6 @@ dbghelp! {
path: PCWSTR,
invade: BOOL
) -> BOOL;
- fn SymCleanup(handle: HANDLE) -> BOOL;
fn StackWalk64(
MachineType: DWORD,
hProcess: HANDLE,
@@ -184,18 +193,6 @@ dbghelp! {
hProcess: HANDLE,
AddrBase: DWORD64
) -> DWORD64;
- fn SymFromAddrW(
- hProcess: HANDLE,
- Address: DWORD64,
- Displacement: PDWORD64,
- Symbol: PSYMBOL_INFOW
- ) -> BOOL;
- fn SymGetLineFromAddrW64(
- hProcess: HANDLE,
- dwAddr: DWORD64,
- pdwDisplacement: PDWORD,
- Line: PIMAGEHLP_LINEW64
- ) -> BOOL;
fn StackWalkEx(
MachineType: DWORD,
hProcess: HANDLE,
@@ -223,6 +220,31 @@ dbghelp! {
pdwDisplacement: PDWORD,
Line: PIMAGEHLP_LINEW64
) -> BOOL;
+ fn SymAddrIncludeInlineTrace(
+ hProcess: HANDLE,
+ Address: DWORD64
+ ) -> DWORD;
+ fn SymQueryInlineTrace(
+ hProcess: HANDLE,
+ StartAddress: DWORD64,
+ StartContext: DWORD,
+ StartRetAddress: DWORD64,
+ CurAddress: DWORD64,
+ CurContext: LPDWORD,
+ CurFrameIndex: LPDWORD
+ ) -> BOOL;
+ fn SymFromAddrW(
+ hProcess: HANDLE,
+ Address: DWORD64,
+ Displacement: PDWORD64,
+ Symbol: PSYMBOL_INFOW
+ ) -> BOOL;
+ fn SymGetLineFromAddrW64(
+ hProcess: HANDLE,
+ dwAddr: DWORD64,
+ pdwDisplacement: PDWORD,
+ Line: PIMAGEHLP_LINEW64
+ ) -> BOOL;
}
}
diff --git a/library/backtrace/src/lib.rs b/library/backtrace/src/lib.rs
index 4615e1f96..44a0bc64e 100644
--- a/library/backtrace/src/lib.rs
+++ b/library/backtrace/src/lib.rs
@@ -134,6 +134,12 @@ cfg_if::cfg_if! {
}
}
+cfg_if::cfg_if! {
+ if #[cfg(all(target_env = "sgx", target_vendor = "fortanix", not(feature = "std")))] {
+ pub use self::backtrace::set_image_base;
+ }
+}
+
#[allow(dead_code)]
struct Bomb {
enabled: bool,
@@ -186,7 +192,14 @@ mod lock {
}
}
-#[cfg(all(windows, not(target_vendor = "uwp")))]
+#[cfg(all(
+ windows,
+ any(
+ target_env = "msvc",
+ all(target_env = "gnu", any(target_arch = "x86", target_arch = "arm"))
+ ),
+ not(target_vendor = "uwp")
+))]
mod dbghelp;
#[cfg(windows)]
mod windows;
diff --git a/library/backtrace/src/print.rs b/library/backtrace/src/print.rs
index 395328a0a..de8569182 100644
--- a/library/backtrace/src/print.rs
+++ b/library/backtrace/src/print.rs
@@ -219,7 +219,7 @@ impl BacktraceFrameFmt<'_, '_, '_> {
#[allow(unused_mut)]
fn print_raw_generic(
&mut self,
- mut frame_ip: *mut c_void,
+ frame_ip: *mut c_void,
symbol_name: Option<SymbolName<'_>>,
filename: Option<BytesOrWideString<'_>>,
lineno: Option<u32>,
@@ -233,15 +233,6 @@ impl BacktraceFrameFmt<'_, '_, '_> {
}
}
- // To reduce TCB size in Sgx enclave, we do not want to implement symbol
- // resolution functionality. Rather, we can print the offset of the
- // address here, which could be later mapped to correct function.
- #[cfg(all(feature = "std", target_env = "sgx", target_vendor = "fortanix"))]
- {
- let image_base = std::os::fortanix_sgx::mem::image_base();
- frame_ip = usize::wrapping_sub(frame_ip as usize, image_base as _) as _;
- }
-
// Print the index of the frame as well as the optional instruction
// pointer of the frame. If we're beyond the first symbol of this frame
// though we just print appropriate whitespace.
diff --git a/library/backtrace/src/symbolize/dbghelp.rs b/library/backtrace/src/symbolize/dbghelp.rs
index 181dba731..8c47d58e8 100644
--- a/library/backtrace/src/symbolize/dbghelp.rs
+++ b/library/backtrace/src/symbolize/dbghelp.rs
@@ -17,7 +17,7 @@
#![allow(bad_style)]
-use super::super::{backtrace::StackFrame, dbghelp, windows::*};
+use super::super::{dbghelp, windows::*};
use super::{BytesOrWideString, ResolveWhat, SymbolName};
use core::char;
use core::ffi::c_void;
@@ -78,54 +78,103 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))
Err(()) => return, // oh well...
};
+ let resolve_inner = if (*dbghelp.dbghelp()).SymAddrIncludeInlineTrace().is_some() {
+ // We are on a version of dbghelp 6.2+, which contains the more modern
+ // Inline APIs.
+ resolve_with_inline
+ } else {
+ // We are on an older version of dbghelp which doesn't contain the Inline
+ // APIs.
+ resolve_legacy
+ };
match what {
- ResolveWhat::Address(_) => resolve_without_inline(&dbghelp, what.address_or_ip(), cb),
- ResolveWhat::Frame(frame) => match &frame.inner.stack_frame {
- StackFrame::New(frame) => resolve_with_inline(&dbghelp, frame, cb),
- StackFrame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb),
- },
+ ResolveWhat::Address(_) => resolve_inner(&dbghelp, what.address_or_ip(), None, cb),
+ ResolveWhat::Frame(frame) => {
+ resolve_inner(&dbghelp, frame.ip(), frame.inner.inline_context(), cb)
+ }
}
}
-unsafe fn resolve_with_inline(
+/// Resolve the address using the legacy dbghelp API.
+///
+/// This should work all the way down to Windows XP. The inline context is
+/// ignored, since this concept was only introduced in dbghelp 6.2+.
+unsafe fn resolve_legacy(
dbghelp: &dbghelp::Init,
- frame: &STACKFRAME_EX,
+ addr: *mut c_void,
+ _inline_context: Option<DWORD>,
cb: &mut dyn FnMut(&super::Symbol),
) {
+ let addr = super::adjust_ip(addr) as DWORD64;
do_resolve(
- |info| {
- dbghelp.SymFromInlineContextW()(
- GetCurrentProcess(),
- super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
- frame.InlineFrameContext,
- &mut 0,
- info,
- )
- },
- |line| {
- dbghelp.SymGetLineFromInlineContextW()(
- GetCurrentProcess(),
- super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
- frame.InlineFrameContext,
- 0,
- &mut 0,
- line,
- )
- },
+ |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr, &mut 0, info),
+ |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr, &mut 0, line),
cb,
)
}
-unsafe fn resolve_without_inline(
+/// Resolve the address using the modern dbghelp APIs.
+///
+/// Note that calling this function requires having dbghelp 6.2+ loaded - and
+/// will panic otherwise.
+unsafe fn resolve_with_inline(
dbghelp: &dbghelp::Init,
addr: *mut c_void,
+ inline_context: Option<DWORD>,
cb: &mut dyn FnMut(&super::Symbol),
) {
- do_resolve(
- |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr as DWORD64, &mut 0, info),
- |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr as DWORD64, &mut 0, line),
- cb,
- )
+ let current_process = GetCurrentProcess();
+
+ let addr = super::adjust_ip(addr) as DWORD64;
+
+ let (inlined_frame_count, inline_context) = if let Some(ic) = inline_context {
+ (0, ic)
+ } else {
+ let mut inlined_frame_count = dbghelp.SymAddrIncludeInlineTrace()(current_process, addr);
+
+ let mut inline_context = 0;
+
+ // If there is are inlined frames but we can't load them for some reason OR if there are no
+ // inlined frames, then we disregard inlined_frame_count and inline_context.
+ if (inlined_frame_count > 0
+ && dbghelp.SymQueryInlineTrace()(
+ current_process,
+ addr,
+ 0,
+ addr,
+ addr,
+ &mut inline_context,
+ &mut 0,
+ ) != TRUE)
+ || inlined_frame_count == 0
+ {
+ inlined_frame_count = 0;
+ inline_context = 0;
+ }
+
+ (inlined_frame_count, inline_context)
+ };
+
+ let last_inline_context = inline_context + 1 + inlined_frame_count;
+
+ for inline_context in inline_context..last_inline_context {
+ do_resolve(
+ |info| {
+ dbghelp.SymFromInlineContextW()(current_process, addr, inline_context, &mut 0, info)
+ },
+ |line| {
+ dbghelp.SymGetLineFromInlineContextW()(
+ current_process,
+ addr,
+ inline_context,
+ 0,
+ &mut 0,
+ line,
+ )
+ },
+ cb,
+ );
+ }
}
unsafe fn do_resolve(
diff --git a/library/backtrace/src/symbolize/gimli.rs b/library/backtrace/src/symbolize/gimli.rs
index 7f1c6a528..3b28bf741 100644
--- a/library/backtrace/src/symbolize/gimli.rs
+++ b/library/backtrace/src/symbolize/gimli.rs
@@ -35,12 +35,14 @@ cfg_if::cfg_if! {
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
+ target_os = "hurd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos",
+ target_os = "aix",
))] {
#[path = "gimli/mmap_unix.rs"]
mod mmap;
@@ -116,8 +118,17 @@ impl<'data> Context<'data> {
dwp: Option<Object<'data>>,
) -> Option<Context<'data>> {
let mut sections = gimli::Dwarf::load(|id| -> Result<_, ()> {
- let data = object.section(stash, id.name()).unwrap_or(&[]);
- Ok(EndianSlice::new(data, Endian))
+ if cfg!(not(target_os = "aix")) {
+ let data = object.section(stash, id.name()).unwrap_or(&[]);
+ Ok(EndianSlice::new(data, Endian))
+ } else {
+ if let Some(name) = id.xcoff_name() {
+ let data = object.section(stash, name).unwrap_or(&[]);
+ Ok(EndianSlice::new(data, Endian))
+ } else {
+ Ok(EndianSlice::new(&[], Endian))
+ }
+ }
})
.ok()?;
@@ -192,6 +203,9 @@ cfg_if::cfg_if! {
))] {
mod macho;
use self::macho::{handle_split_dwarf, Object};
+ } else if #[cfg(target_os = "aix")] {
+ mod xcoff;
+ use self::xcoff::{handle_split_dwarf, Object};
} else {
mod elf;
use self::elf::{handle_split_dwarf, Object};
@@ -218,6 +232,7 @@ cfg_if::cfg_if! {
target_os = "linux",
target_os = "fuchsia",
target_os = "freebsd",
+ target_os = "hurd",
target_os = "openbsd",
target_os = "netbsd",
all(target_os = "android", feature = "dl_iterate_phdr"),
@@ -234,6 +249,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_os = "haiku")] {
mod libs_haiku;
use libs_haiku::native_libraries;
+ } else if #[cfg(target_os = "aix")] {
+ mod libs_aix;
+ use libs_aix::native_libraries;
} else {
// Everything else should doesn't know how to load native libraries.
fn native_libraries() -> Vec<Library> {
@@ -261,6 +279,13 @@ struct Cache {
struct Library {
name: OsString,
+ #[cfg(target_os = "aix")]
+ /// On AIX, the library mmapped can be a member of a big-archive file.
+ /// For example, with a big-archive named libfoo.a containing libbar.so,
+ /// one can use `dlopen("libfoo.a(libbar.so)", RTLD_MEMBER | RTLD_LAZY)`
+ /// to use the `libbar.so` library. In this case, only `libbar.so` is
+ /// mmapped, not the whole `libfoo.a`.
+ member_name: OsString,
/// Segments of this library loaded into memory, and where they're loaded.
segments: Vec<LibrarySegment>,
/// The "bias" of this library, typically where it's loaded into memory.
@@ -280,6 +305,19 @@ struct LibrarySegment {
len: usize,
}
+#[cfg(target_os = "aix")]
+fn create_mapping(lib: &Library) -> Option<Mapping> {
+ let name = &lib.name;
+ let member_name = &lib.member_name;
+ Mapping::new(name.as_ref(), member_name)
+}
+
+#[cfg(not(target_os = "aix"))]
+fn create_mapping(lib: &Library) -> Option<Mapping> {
+ let name = &lib.name;
+ Mapping::new(name.as_ref())
+}
+
// unsafe because this is required to be externally synchronized
pub unsafe fn clear_symbol_cache() {
Cache::with_global(|cache| cache.mappings.clear());
@@ -360,8 +398,7 @@ impl Cache {
// When the mapping is not in the cache, create a new mapping,
// insert it into the front of the cache, and evict the oldest cache
// entry if necessary.
- let name = &self.libraries[lib].name;
- let mapping = Mapping::new(name.as_ref())?;
+ let mapping = create_mapping(&self.libraries[lib])?;
if self.mappings.len() == MAPPINGS_CACHE_SIZE {
self.mappings.pop();
diff --git a/library/backtrace/src/symbolize/gimli/elf.rs b/library/backtrace/src/symbolize/gimli/elf.rs
index b0eec0762..906a30054 100644
--- a/library/backtrace/src/symbolize/gimli/elf.rs
+++ b/library/backtrace/src/symbolize/gimli/elf.rs
@@ -308,7 +308,7 @@ const DEBUG_PATH: &[u8] = b"/usr/lib/debug";
fn debug_path_exists() -> bool {
cfg_if::cfg_if! {
- if #[cfg(any(target_os = "freebsd", target_os = "linux"))] {
+ if #[cfg(any(target_os = "freebsd", target_os = "hurd", target_os = "linux"))] {
use core::sync::atomic::{AtomicU8, Ordering};
static DEBUG_PATH_EXISTS: AtomicU8 = AtomicU8::new(0);
diff --git a/library/backtrace/src/symbolize/gimli/libs_aix.rs b/library/backtrace/src/symbolize/gimli/libs_aix.rs
new file mode 100644
index 000000000..8cac11d4d
--- /dev/null
+++ b/library/backtrace/src/symbolize/gimli/libs_aix.rs
@@ -0,0 +1,74 @@
+use super::mystd::borrow::ToOwned;
+use super::mystd::env;
+use super::mystd::ffi::{CStr, OsStr};
+use super::mystd::io::Error;
+use super::mystd::os::unix::prelude::*;
+use super::xcoff;
+use super::{Library, LibrarySegment, Vec};
+use alloc::vec;
+use core::mem;
+
+const EXE_IMAGE_BASE: u64 = 0x100000000;
+
+/// On AIX, we use `loadquery` with `L_GETINFO` flag to query libraries mmapped.
+/// See https://www.ibm.com/docs/en/aix/7.2?topic=l-loadquery-subroutine for
+/// detailed information of `loadquery`.
+pub(super) fn native_libraries() -> Vec<Library> {
+ let mut ret = Vec::new();
+ unsafe {
+ let mut buffer = vec![mem::zeroed::<libc::ld_info>(); 64];
+ loop {
+ if libc::loadquery(
+ libc::L_GETINFO,
+ buffer.as_mut_ptr() as *mut libc::c_char,
+ (mem::size_of::<libc::ld_info>() * buffer.len()) as u32,
+ ) != -1
+ {
+ break;
+ } else {
+ match Error::last_os_error().raw_os_error() {
+ Some(libc::ENOMEM) => {
+ buffer.resize(buffer.len() * 2, mem::zeroed::<libc::ld_info>());
+ }
+ Some(_) => {
+ // If other error occurs, return empty libraries.
+ return Vec::new();
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ let mut current = buffer.as_mut_ptr();
+ loop {
+ let text_base = (*current).ldinfo_textorg as usize;
+ let filename_ptr: *const libc::c_char = &(*current).ldinfo_filename[0];
+ let bytes = CStr::from_ptr(filename_ptr).to_bytes();
+ let member_name_ptr = filename_ptr.offset((bytes.len() + 1) as isize);
+ let mut filename = OsStr::from_bytes(bytes).to_owned();
+ if text_base == EXE_IMAGE_BASE as usize {
+ if let Ok(exe) = env::current_exe() {
+ filename = exe.into_os_string();
+ }
+ }
+ let bytes = CStr::from_ptr(member_name_ptr).to_bytes();
+ let member_name = OsStr::from_bytes(bytes).to_owned();
+ if let Some(image) = xcoff::parse_image(filename.as_ref(), &member_name) {
+ ret.push(Library {
+ name: filename,
+ member_name,
+ segments: vec![LibrarySegment {
+ stated_virtual_memory_address: image.base as usize,
+ len: image.size,
+ }],
+ bias: (text_base + image.offset).wrapping_sub(image.base as usize),
+ });
+ }
+ if (*current).ldinfo_next == 0 {
+ break;
+ }
+ current = (current as *mut libc::c_char).offset((*current).ldinfo_next as isize)
+ as *mut libc::ld_info;
+ }
+ }
+ return ret;
+}
diff --git a/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs b/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
index 9f0304ce8..518512fff 100644
--- a/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
+++ b/library/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
@@ -18,14 +18,18 @@ pub(super) fn native_libraries() -> Vec<Library> {
}
fn infer_current_exe(base_addr: usize) -> OsString {
- if let Ok(entries) = super::parse_running_mmaps::parse_maps() {
- let opt_path = entries
- .iter()
- .find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0)
- .map(|e| e.pathname())
- .cloned();
- if let Some(path) = opt_path {
- return path;
+ cfg_if::cfg_if! {
+ if #[cfg(not(target_os = "hurd"))] {
+ if let Ok(entries) = super::parse_running_mmaps::parse_maps() {
+ let opt_path = entries
+ .iter()
+ .find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0)
+ .map(|e| e.pathname())
+ .cloned();
+ if let Some(path) = opt_path {
+ return path;
+ }
+ }
}
}
env::current_exe().map(|e| e.into()).unwrap_or_default()
diff --git a/library/backtrace/src/symbolize/gimli/xcoff.rs b/library/backtrace/src/symbolize/gimli/xcoff.rs
new file mode 100644
index 000000000..dd308840f
--- /dev/null
+++ b/library/backtrace/src/symbolize/gimli/xcoff.rs
@@ -0,0 +1,186 @@
+use super::mystd::ffi::{OsStr, OsString};
+use super::mystd::os::unix::ffi::OsStrExt;
+use super::mystd::str;
+use super::{gimli, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec};
+use alloc::sync::Arc;
+use core::ops::Deref;
+use object::read::archive::ArchiveFile;
+use object::read::xcoff::{FileHeader, SectionHeader, XcoffFile, XcoffSymbol};
+use object::Object as _;
+use object::ObjectSection as _;
+use object::ObjectSymbol as _;
+use object::SymbolFlags;
+
+#[cfg(target_pointer_width = "32")]
+type Xcoff = object::xcoff::FileHeader32;
+#[cfg(target_pointer_width = "64")]
+type Xcoff = object::xcoff::FileHeader64;
+
+impl Mapping {
+ pub fn new(path: &Path, member_name: &OsString) -> Option<Mapping> {
+ let map = super::mmap(path)?;
+ Mapping::mk(map, |data, stash| {
+ if member_name.is_empty() {
+ Context::new(stash, Object::parse(data)?, None, None)
+ } else {
+ let archive = ArchiveFile::parse(data).ok()?;
+ for member in archive
+ .members()
+ .filter_map(|m| m.ok())
+ .filter(|m| OsStr::from_bytes(m.name()) == member_name)
+ {
+ let member_data = member.data(data).ok()?;
+ if let Some(obj) = Object::parse(member_data) {
+ return Context::new(stash, obj, None, None);
+ }
+ }
+ None
+ }
+ })
+ }
+}
+
+struct ParsedSym<'a> {
+ address: u64,
+ size: u64,
+ name: &'a str,
+}
+
+pub struct Object<'a> {
+ syms: Vec<ParsedSym<'a>>,
+ file: XcoffFile<'a, Xcoff>,
+}
+
+pub struct Image {
+ pub offset: usize,
+ pub base: u64,
+ pub size: usize,
+}
+
+pub fn parse_xcoff(data: &[u8]) -> Option<Image> {
+ let mut offset = 0;
+ let header = Xcoff::parse(data, &mut offset).ok()?;
+ let _ = header.aux_header(data, &mut offset).ok()?;
+ let sections = header.sections(data, &mut offset).ok()?;
+ if let Some(section) = sections.iter().find(|s| {
+ if let Ok(name) = str::from_utf8(&s.s_name()[0..5]) {
+ name == ".text"
+ } else {
+ false
+ }
+ }) {
+ Some(Image {
+ offset: section.s_scnptr() as usize,
+ base: section.s_paddr() as u64,
+ size: section.s_size() as usize,
+ })
+ } else {
+ None
+ }
+}
+
+pub fn parse_image(path: &Path, member_name: &OsString) -> Option<Image> {
+ let map = super::mmap(path)?;
+ let data = map.deref();
+ if member_name.is_empty() {
+ return parse_xcoff(data);
+ } else {
+ let archive = ArchiveFile::parse(data).ok()?;
+ for member in archive
+ .members()
+ .filter_map(|m| m.ok())
+ .filter(|m| OsStr::from_bytes(m.name()) == member_name)
+ {
+ let member_data = member.data(data).ok()?;
+ if let Some(image) = parse_xcoff(member_data) {
+ return Some(image);
+ }
+ }
+ None
+ }
+}
+
+impl<'a> Object<'a> {
+ fn get_concrete_size(file: &XcoffFile<'a, Xcoff>, sym: &XcoffSymbol<'a, '_, Xcoff>) -> u64 {
+ match sym.flags() {
+ SymbolFlags::Xcoff {
+ n_sclass: _,
+ x_smtyp: _,
+ x_smclas: _,
+ containing_csect: Some(index),
+ } => {
+ if let Ok(tgt_sym) = file.symbol_by_index(index) {
+ Self::get_concrete_size(file, &tgt_sym)
+ } else {
+ 0
+ }
+ }
+ _ => sym.size(),
+ }
+ }
+
+ fn parse(data: &'a [u8]) -> Option<Object<'a>> {
+ let file = XcoffFile::parse(data).ok()?;
+ let mut syms = file
+ .symbols()
+ .filter_map(|sym| {
+ let name = sym.name().map_or("", |v| v);
+ let address = sym.address();
+ let size = Self::get_concrete_size(&file, &sym);
+ if name == ".text" || name == ".data" {
+ // We don't want to include ".text" and ".data" symbols.
+ // If they are included, since their ranges cover other
+ // symbols, when searching a symbol for a given address,
+ // ".text" or ".data" is returned. That's not what we expect.
+ None
+ } else {
+ Some(ParsedSym {
+ address,
+ size,
+ name,
+ })
+ }
+ })
+ .collect::<Vec<_>>();
+ syms.sort_by_key(|s| s.address);
+ Some(Object { syms, file })
+ }
+
+ pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
+ Some(self.file.section_by_name(name)?.data().ok()?)
+ }
+
+ pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
+ // Symbols, except ".text" and ".data", are sorted and are not overlapped each other,
+ // so we can just perform a binary search here.
+ let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) {
+ Ok(i) => i,
+ Err(i) => i.checked_sub(1)?,
+ };
+ let sym = self.syms.get(i)?;
+ if (sym.address..sym.address + sym.size).contains(&addr) {
+ // On AIX, for a function call, for example, `foo()`, we have
+ // two symbols `foo` and `.foo`. `foo` references the function
+ // descriptor and `.foo` references the function entry.
+ // See https://www.ibm.com/docs/en/xl-fortran-aix/16.1.0?topic=calls-linkage-convention-function
+ // for more information.
+ // We trim the prefix `.` here, so that the rust demangler can work
+ // properly.
+ Some(sym.name.trim_start_matches(".").as_bytes())
+ } else {
+ None
+ }
+ }
+
+ pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
+ None
+ }
+}
+
+pub(super) fn handle_split_dwarf<'data>(
+ _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
+ _stash: &'data Stash,
+ _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
+) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
+ None
+}
diff --git a/library/backtrace/src/windows.rs b/library/backtrace/src/windows.rs
index 92c2b2e66..13287f7c3 100644
--- a/library/backtrace/src/windows.rs
+++ b/library/backtrace/src/windows.rs
@@ -19,6 +19,9 @@ cfg_if::cfg_if! {
pub use self::winapi::PUNWIND_HISTORY_TABLE;
#[cfg(target_pointer_width = "64")]
pub use self::winapi::PRUNTIME_FUNCTION;
+ pub use self::winapi::PEXCEPTION_ROUTINE;
+ #[cfg(target_pointer_width = "64")]
+ pub use self::winapi::PKNONVOLATILE_CONTEXT_POINTERS;
mod winapi {
pub use winapi::ctypes::*;
@@ -35,6 +38,22 @@ cfg_if::cfg_if! {
pub use winapi::um::tlhelp32::*;
pub use winapi::um::winbase::*;
pub use winapi::um::winnt::*;
+
+ // Work around winapi not having this function on aarch64.
+ #[cfg(target_arch = "aarch64")]
+ #[link(name = "kernel32")]
+ extern "system" {
+ pub fn RtlVirtualUnwind(
+ HandlerType: ULONG,
+ ImageBase: ULONG64,
+ ControlPc: ULONG64,
+ FunctionEntry: PRUNTIME_FUNCTION,
+ ContextRecord: PCONTEXT,
+ HandlerData: *mut PVOID,
+ EstablisherFrame: PULONG64,
+ ContextPointers: PKNONVOLATILE_CONTEXT_POINTERS
+ ) -> PEXCEPTION_ROUTINE;
+ }
}
} else {
pub use core::ffi::c_void;
@@ -45,6 +64,9 @@ cfg_if::cfg_if! {
pub type PRUNTIME_FUNCTION = *mut c_void;
#[cfg(target_pointer_width = "64")]
pub type PUNWIND_HISTORY_TABLE = *mut c_void;
+ pub type PEXCEPTION_ROUTINE = *mut c_void;
+ #[cfg(target_pointer_width = "64")]
+ pub type PKNONVOLATILE_CONTEXT_POINTERS = *mut c_void;
}
}
@@ -359,6 +381,7 @@ ffi! {
pub type LPCSTR = *const i8;
pub type PWSTR = *mut u16;
pub type WORD = u16;
+ pub type USHORT = u16;
pub type ULONG = u32;
pub type ULONG64 = u64;
pub type WCHAR = u16;
@@ -370,6 +393,8 @@ ffi! {
pub type LPVOID = *mut c_void;
pub type LPCVOID = *const c_void;
pub type LPMODULEENTRY32W = *mut MODULEENTRY32W;
+ pub type PULONG = *mut ULONG;
+ pub type PULONG64 = *mut ULONG64;
#[link(name = "kernel32")]
extern "system" {
@@ -435,6 +460,33 @@ ffi! {
lpme: LPMODULEENTRY32W,
) -> BOOL;
}
+
+ #[link(name = "ntdll")]
+ extern "system" {
+ pub fn RtlCaptureStackBackTrace(
+ FramesToSkip: ULONG,
+ FramesToCapture: ULONG,
+ BackTrace: *mut PVOID,
+ BackTraceHash: PULONG,
+ ) -> USHORT;
+ }
+}
+
+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
+ffi! {
+ #[link(name = "kernel32")]
+ extern "system" {
+ pub fn RtlVirtualUnwind(
+ HandlerType: ULONG,
+ ImageBase: ULONG64,
+ ControlPc: ULONG64,
+ FunctionEntry: PRUNTIME_FUNCTION,
+ ContextRecord: PCONTEXT,
+ HandlerData: *mut PVOID,
+ EstablisherFrame: PULONG64,
+ ContextPointers: PKNONVOLATILE_CONTEXT_POINTERS
+ ) -> PEXCEPTION_ROUTINE;
+ }
}
#[cfg(target_pointer_width = "64")]
diff --git a/library/backtrace/tests/accuracy/main.rs b/library/backtrace/tests/accuracy/main.rs
index 149203a1b..79b2d3797 100644
--- a/library/backtrace/tests/accuracy/main.rs
+++ b/library/backtrace/tests/accuracy/main.rs
@@ -31,6 +31,8 @@ fn doit() {
dir.push("dylib_dep.dll");
} else if cfg!(target_os = "macos") {
dir.push("libdylib_dep.dylib");
+ } else if cfg!(target_os = "aix") {
+ dir.push("libdylib_dep.a");
} else {
dir.push("libdylib_dep.so");
}
@@ -103,7 +105,7 @@ fn verify(filelines: &[Pos]) {
loop {
let sym = match symbols.next() {
Some(sym) => sym,
- None => panic!("failed to find {}:{}", file, line),
+ None => panic!("failed to find {file}:{line}"),
};
if let Some(filename) = sym.filename() {
if let Some(lineno) = sym.lineno() {
diff --git a/library/backtrace/tests/sgx-image-base.rs b/library/backtrace/tests/sgx-image-base.rs
new file mode 100644
index 000000000..c29a8b67a
--- /dev/null
+++ b/library/backtrace/tests/sgx-image-base.rs
@@ -0,0 +1,56 @@
+#![cfg(all(target_env = "sgx", target_vendor = "fortanix"))]
+#![feature(sgx_platform)]
+
+#[cfg(feature = "std")]
+#[test]
+fn sgx_image_base_with_std() {
+ use backtrace::trace;
+
+ let image_base = std::os::fortanix_sgx::mem::image_base();
+
+ let mut frame_ips = Vec::new();
+ trace(|frame| {
+ frame_ips.push(frame.ip());
+ true
+ });
+
+ assert!(frame_ips.len() > 0);
+ for ip in frame_ips {
+ let ip: u64 = ip as _;
+ assert!(ip < image_base);
+ }
+}
+
+#[cfg(not(feature = "std"))]
+#[test]
+fn sgx_image_base_no_std() {
+ use backtrace::trace_unsynchronized;
+
+ fn guess_image_base() -> u64 {
+ let mut top_frame_ip = None;
+ unsafe {
+ trace_unsynchronized(|frame| {
+ top_frame_ip = Some(frame.ip());
+ false
+ });
+ }
+ top_frame_ip.unwrap() as u64 & 0xFFFFFF000000
+ }
+
+ let image_base = guess_image_base();
+ backtrace::set_image_base(image_base as _);
+
+ let mut frame_ips = Vec::new();
+ unsafe {
+ trace_unsynchronized(|frame| {
+ frame_ips.push(frame.ip());
+ true
+ });
+ }
+
+ assert!(frame_ips.len() > 0);
+ for ip in frame_ips {
+ let ip: u64 = ip as _;
+ assert!(ip < image_base);
+ }
+}
diff --git a/library/backtrace/tests/smoke.rs b/library/backtrace/tests/smoke.rs
index 683a6f0db..715f567f3 100644
--- a/library/backtrace/tests/smoke.rs
+++ b/library/backtrace/tests/smoke.rs
@@ -1,6 +1,27 @@
use backtrace::Frame;
use std::thread;
+fn get_actual_fn_pointer(fp: usize) -> usize {
+ // On AIX, the function name references a function descriptor.
+ // A function descriptor consists of (See https://reviews.llvm.org/D62532)
+ // * The address of the entry point of the function.
+ // * The TOC base address for the function.
+ // * The environment pointer.
+ // Deref `fp` directly so that we can get the address of `fp`'s
+ // entry point in text section.
+ //
+ // For TOC, one can find more information in
+ // https://www.ibm.com/docs/en/aix/7.2?topic=program-understanding-programming-toc
+ if cfg!(target_os = "aix") {
+ unsafe {
+ let actual_fn_entry = *(fp as *const usize);
+ actual_fn_entry
+ }
+ } else {
+ fp
+ }
+}
+
#[test]
// FIXME: shouldn't ignore this test on i686-msvc, unsure why it's failing
#[cfg_attr(all(target_arch = "x86", target_env = "msvc"), ignore)]
@@ -20,7 +41,7 @@ fn smoke_test_frames() {
// Various platforms have various bits of weirdness about their
// backtraces. To find a good starting spot let's search through the
// frames
- let target = frame_4 as usize;
+ let target = get_actual_fn_pointer(frame_4 as usize);
let offset = v
.iter()
.map(|frame| frame.symbol_address() as usize)
@@ -39,7 +60,7 @@ fn smoke_test_frames() {
assert_frame(
frames.next().unwrap(),
- frame_4 as usize,
+ get_actual_fn_pointer(frame_4 as usize),
"frame_4",
"tests/smoke.rs",
start_line + 6,
@@ -47,7 +68,7 @@ fn smoke_test_frames() {
);
assert_frame(
frames.next().unwrap(),
- frame_3 as usize,
+ get_actual_fn_pointer(frame_3 as usize),
"frame_3",
"tests/smoke.rs",
start_line + 3,
@@ -55,7 +76,7 @@ fn smoke_test_frames() {
);
assert_frame(
frames.next().unwrap(),
- frame_2 as usize,
+ get_actual_fn_pointer(frame_2 as usize),
"frame_2",
"tests/smoke.rs",
start_line + 2,
@@ -63,7 +84,7 @@ fn smoke_test_frames() {
);
assert_frame(
frames.next().unwrap(),
- frame_1 as usize,
+ get_actual_fn_pointer(frame_1 as usize),
"frame_1",
"tests/smoke.rs",
start_line + 1,
@@ -71,7 +92,7 @@ fn smoke_test_frames() {
);
assert_frame(
frames.next().unwrap(),
- smoke_test_frames as usize,
+ get_actual_fn_pointer(smoke_test_frames as usize),
"smoke_test_frames",
"",
0,
@@ -150,9 +171,7 @@ fn smoke_test_frames() {
if cfg!(debug_assertions) {
assert!(
name.contains(expected_name),
- "didn't find `{}` in `{}`",
- expected_name,
- name
+ "didn't find `{expected_name}` in `{name}`"
);
}
@@ -164,18 +183,13 @@ fn smoke_test_frames() {
if !expected_file.is_empty() {
assert!(
file.ends_with(expected_file),
- "{:?} didn't end with {:?}",
- file,
- expected_file
+ "{file:?} didn't end with {expected_file:?}"
);
}
if expected_line != 0 {
assert!(
line == expected_line,
- "bad line number on frame for `{}`: {} != {}",
- expected_name,
- line,
- expected_line
+ "bad line number on frame for `{expected_name}`: {line} != {expected_line}"
);
}
@@ -185,10 +199,7 @@ fn smoke_test_frames() {
if expected_col != 0 {
assert!(
col == expected_col,
- "bad column number on frame for `{}`: {} != {}",
- expected_name,
- col,
- expected_col
+ "bad column number on frame for `{expected_name}`: {col} != {expected_col}",
);
}
}