diff --git a/.github/workflows/arm64e-stage1-prerelease.yml b/.github/workflows/arm64e-stage1-prerelease.yml new file mode 100644 index 0000000000000..1d17720a80cb3 --- /dev/null +++ b/.github/workflows/arm64e-stage1-prerelease.yml @@ -0,0 +1,417 @@ +name: arm64e Stage1 Prerelease + +on: + workflow_dispatch: + inputs: + source_ref: + description: Branch, tag, or SHA to build. + required: true + default: carry/cypherair-arm64e-toolchain + type: string + release_tag_prefix: + description: Unique release tag prefix. + required: true + default: rust-arm64e-stage1 + type: string + create_release: + description: Publish the GitHub prerelease instead of uploading dry-run artifacts. + required: true + default: true + type: boolean + push: + branches: + - carry/cypherair-arm64e-toolchain + schedule: + - cron: "17 10 * * 1" + +permissions: + contents: write + id-token: write + attestations: write + +concurrency: + group: arm64e-stage1-prerelease-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +env: + ASSET_BASE: rust-stage1-arm64e-apple-darwin + INTEGRATION_BRANCH: carry/cypherair-arm64e-toolchain + +jobs: + publish-stage1: + if: github.repository == 'cypherair/rust' + runs-on: macos-26 + timeout-minutes: 240 + defaults: + run: + shell: bash + steps: + - name: Resolve release settings + id: settings + env: + INPUT_SOURCE_REF: ${{ github.event.inputs.source_ref }} + INPUT_RELEASE_TAG_PREFIX: ${{ github.event.inputs.release_tag_prefix }} + INPUT_CREATE_RELEASE: ${{ github.event.inputs.create_release }} + run: | + set -euo pipefail + + case "$GITHUB_EVENT_NAME" in + workflow_dispatch) + SOURCE_REF="${INPUT_SOURCE_REF:-$INTEGRATION_BRANCH}" + RELEASE_TAG_PREFIX="${INPUT_RELEASE_TAG_PREFIX:-rust-arm64e-stage1}" + CREATE_RELEASE="${INPUT_CREATE_RELEASE:-true}" + ;; + push) + SOURCE_REF="$GITHUB_REF" + RELEASE_TAG_PREFIX="rust-arm64e-stage1" + CREATE_RELEASE="true" + ;; + schedule) + SOURCE_REF="$INTEGRATION_BRANCH" + RELEASE_TAG_PREFIX="rust-arm64e-stage1" + CREATE_RELEASE="true" + ;; + *) + echo "::error::Unsupported event: $GITHUB_EVENT_NAME" + exit 1 + ;; + esac + + case "$RELEASE_TAG_PREFIX" in + *[!A-Za-z0-9._-]*) + echo "::error::release_tag_prefix may only contain letters, numbers, dots, underscores, and hyphens." + exit 1 + ;; + esac + + { + echo "SOURCE_REF=$SOURCE_REF" + echo "RELEASE_TAG_PREFIX=$RELEASE_TAG_PREFIX" + echo "CREATE_RELEASE=$CREATE_RELEASE" + } >> "$GITHUB_ENV" + { + echo "source_ref=$SOURCE_REF" + echo "release_tag_prefix=$RELEASE_TAG_PREFIX" + echo "create_release=$CREATE_RELEASE" + } >> "$GITHUB_OUTPUT" + + - name: Checkout source + uses: actions/checkout@v5 + with: + fetch-depth: 2 + ref: ${{ steps.settings.outputs.source_ref }} + submodules: recursive + + - name: Select Xcode 26.4.1 + run: | + set -euo pipefail + + if [ -n "${XCODE_26_DEVELOPER_DIR:-}" ] && [ -d "${XCODE_26_DEVELOPER_DIR}" ]; then + SELECTED_DEVELOPER_DIR="${XCODE_26_DEVELOPER_DIR}" + elif [ -d "/Applications/Xcode_26.4.1.app/Contents/Developer" ]; then + SELECTED_DEVELOPER_DIR="/Applications/Xcode_26.4.1.app/Contents/Developer" + else + echo "::error::Xcode 26.4.1 was not found on this runner." + exit 1 + fi + + echo "DEVELOPER_DIR=${SELECTED_DEVELOPER_DIR}" >> "$GITHUB_ENV" + export DEVELOPER_DIR="${SELECTED_DEVELOPER_DIR}" + xcodebuild -version + + - name: Fetch fork main for bootstrap ancestry checks + run: | + git fetch --no-tags --prune --depth=1 origin \ + +refs/heads/main:refs/remotes/origin/main + + - name: Show runner and source environment + run: | + set -euo pipefail + mkdir -p /tmp/arm64e-stage1-diagnostics + { + echo "event=${GITHUB_EVENT_NAME}" + echo "source_ref=${SOURCE_REF}" + sw_vers + uname -a + df -h + git show -s --format='HEAD: %H %P %ae %s' HEAD + git show -s --format='origin/main: %H %P %ae %s' refs/remotes/origin/main + python3 --version + clang --version + rustc -vV || true + } | tee /tmp/arm64e-stage1-diagnostics/runner-fingerprint.txt + + - name: Run targeted arm64e validation + run: | + set -euo pipefail + python3 x.py check compiler/rustc_codegen_ssa compiler/rustc_codegen_llvm --stage 1 + python3 x.py test --stage 1 --force-rerun \ + tests/codegen-llvm/arm64e-apple-ptrauth*.rs \ + tests/assembly-llvm/arm64e-apple-ptrauth-calls.rs \ + tests/assembly-llvm/targets/targets-macho.rs \ + tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.rs \ + tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.rs + + - name: Build stage1 compiler and Apple std payload + run: | + set -euo pipefail + HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')" + python3 x.py build compiler/rustc library/std library/proc_macro --stage 1 --target "${HOST_TRIPLE},arm64e-apple-darwin" + + - name: Package minimal stage1 toolchain + run: | + set -euo pipefail + + HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')" + STAGE1="build/${HOST_TRIPLE}/stage1" + if [ ! -x "$STAGE1/bin/rustc" ]; then + echo "::error::stage1 rustc missing at $STAGE1/bin/rustc" + exit 1 + fi + + RELEASE_TIMESTAMP="$(date -u +"%Y%m%dT%H%M%SZ")" + SHORT_SHA="$(git rev-parse --short=7 HEAD)" + RUN_ATTEMPT="${GITHUB_RUN_ATTEMPT:-1}" + RELEASE_TAG="${RELEASE_TAG_PREFIX}-${RELEASE_TIMESTAMP}-${SHORT_SHA}-r${GITHUB_RUN_ID}-a${RUN_ATTEMPT}" + RELEASE_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}" + WORKFLOW_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + RELEASE_TITLE="Rust arm64e stage1 toolchain (${RELEASE_TAG})" + + mkdir -p dist/stage1-arm64e-patch + rsync -a --delete "$STAGE1/" dist/stage1-arm64e-patch/ + find dist/stage1-arm64e-patch -name '*.dSYM' -prune -exec rm -rf {} + + + RUST_SRC_DEST="dist/stage1-arm64e-patch/lib/rustlib/src/rust" + rm -rf "$RUST_SRC_DEST" + mkdir -p "$RUST_SRC_DEST/src/llvm-project" + rsync -a --delete library "$RUST_SRC_DEST/" + rsync -a --delete src/llvm-project/libunwind "$RUST_SRC_DEST/src/llvm-project/" + test -d "$RUST_SRC_DEST/library" + test -d "$RUST_SRC_DEST/src/llvm-project/libunwind" + du -sh "$RUST_SRC_DEST" | tee /tmp/arm64e-stage1-diagnostics/rust-src-size.txt + + "./dist/stage1-arm64e-patch/bin/rustc" --print target-list | tee /tmp/arm64e-stage1-diagnostics/target-list.txt + for target in arm64e-apple-darwin arm64e-apple-ios arm64e-apple-tvos arm64e-apple-visionos; do + grep -qx "$target" /tmp/arm64e-stage1-diagnostics/target-list.txt + done + HOST_LIBDIR="$(./dist/stage1-arm64e-patch/bin/rustc --print target-libdir --target "$HOST_TRIPLE")" + test -n "$(find "$HOST_LIBDIR" -maxdepth 1 -name 'libstd-*.rlib' -print -quit)" + test -n "$(find "$HOST_LIBDIR" -maxdepth 1 -name 'libproc_macro-*.rlib' -print -quit)" + test -d "$(./dist/stage1-arm64e-patch/bin/rustc --print sysroot)/lib/rustlib/src/rust/library" + + cat > /tmp/arm64e-stage1-diagnostics/hello.rs <<'EOF' + fn main() { + println!("hello-stage1-arm64e"); + } + EOF + "./dist/stage1-arm64e-patch/bin/rustc" \ + /tmp/arm64e-stage1-diagnostics/hello.rs \ + --target arm64e-apple-darwin \ + -O \ + -o /tmp/arm64e-stage1-diagnostics/hello + /usr/bin/file /tmp/arm64e-stage1-diagnostics/hello | tee /tmp/arm64e-stage1-diagnostics/hello-file.txt + /tmp/arm64e-stage1-diagnostics/hello | tee /tmp/arm64e-stage1-diagnostics/hello-run.txt + + SMOKE_ROOT="/tmp/arm64e-stage1-diagnostics/generic-array-smoke" + STAGE1_RUSTC="$PWD/dist/stage1-arm64e-patch/bin/rustc" + rm -rf "$SMOKE_ROOT" + mkdir -p "$SMOKE_ROOT/src" + cat > "$SMOKE_ROOT/Cargo.toml" <<'EOF' + [package] + name = "generic-array-arm64e-smoke" + version = "0.0.0" + edition = "2021" + + [dependencies] + generic-array = "=1.4.1" + rustversion = "=1.0.22" + EOF + cat > "$SMOKE_ROOT/src/main.rs" <<'EOF' + use generic_array::{typenum::U16, GenericArray}; + + fn main() { + let mut bytes = GenericArray::::default(); + bytes[0] = 42; + println!("{}", bytes[0]); + } + EOF + rustup toolchain install nightly --profile minimal + cargo +nightly generate-lockfile --manifest-path "$SMOKE_ROOT/Cargo.toml" + for smoke_target in arm64e-apple-darwin arm64e-apple-ios arm64e-apple-visionos; do + echo "=== generic-array smoke: ${smoke_target} ===" + env \ + CARGO_TARGET_DIR="$SMOKE_ROOT/target-${smoke_target}" \ + RUSTC="$STAGE1_RUSTC" \ + cargo +nightly build --locked -Zbuild-std \ + --target "$smoke_target" \ + --manifest-path "$SMOKE_ROOT/Cargo.toml" \ + 2>&1 | tee "/tmp/arm64e-stage1-diagnostics/generic-array-smoke-${smoke_target}.log" + done + + bsdtar -cf - -C dist stage1-arm64e-patch | zstd -T0 -19 -o "${ASSET_BASE}.tar.zst" + shasum -a 256 "${ASSET_BASE}.tar.zst" > "${ASSET_BASE}.sha256" + + STAGE1_RUSTC_VERBOSE="$(./dist/stage1-arm64e-patch/bin/rustc -vV)" + XCODE_VERSION="$(xcodebuild -version | tr '\n' ' ' | sed 's/ */ /g; s/ $//')" + BUILT_AT="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + TOOLCHAIN_SIZE_BYTES="$(stat -f%z "${ASSET_BASE}.tar.zst")" + UPSTREAM_MAIN_SHA="$(git rev-parse refs/remotes/origin/main)" + export STAGE1_RUSTC_VERBOSE + + python3 - <> "$GITHUB_ENV" + + - name: Generate release notes + run: | + cat > release-notes.md </dev/null 2>&1; then + echo "::error::Release tag already exists: $RELEASE_TAG" + exit 1 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag "$RELEASE_TAG" "$(git rev-parse HEAD)" + git push origin "refs/tags/$RELEASE_TAG" + + - name: Create draft prerelease + if: env.CREATE_RELEASE == 'true' + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release create "$RELEASE_TAG" \ + --verify-tag \ + --target "$(git rev-parse HEAD)" \ + --title "$RELEASE_TITLE" \ + --notes-file release-notes.md \ + --prerelease \ + --latest=false \ + --draft + + - name: Upload prerelease assets + if: env.CREATE_RELEASE == 'true' + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release upload "$RELEASE_TAG" \ + "${ASSET_BASE}.tar.zst" \ + "${ASSET_BASE}.sha256" \ + "${ASSET_BASE}.json" \ + arm64e-stage1-diagnostics.tar.zst + + - name: Publish draft prerelease + if: env.CREATE_RELEASE == 'true' + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh release edit "$RELEASE_TAG" --draft=false --prerelease --latest=false + + - name: Cleanup failed draft release/tag + if: ${{ failure() && env.CREATE_RELEASE == 'true' }} + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + set +e + if [ -z "${RELEASE_TAG:-}" ]; then + exit 0 + fi + release_is_draft="$(gh release view "$RELEASE_TAG" -R "$GITHUB_REPOSITORY" --json isDraft --jq '.isDraft' 2>/dev/null)" + if [ "$release_is_draft" = "true" ]; then + gh release delete "$RELEASE_TAG" -R "$GITHUB_REPOSITORY" --cleanup-tag --yes + exit 0 + fi + git push origin ":refs/tags/$RELEASE_TAG" 2>/dev/null || true diff --git a/.github/workflows/arm64e-upstream-sync-prep.yml b/.github/workflows/arm64e-upstream-sync-prep.yml new file mode 100644 index 0000000000000..d4c2b5039abd2 --- /dev/null +++ b/.github/workflows/arm64e-upstream-sync-prep.yml @@ -0,0 +1,130 @@ +name: arm64e Upstream Sync Prep + +on: + workflow_dispatch: + inputs: + integration_branch: + description: CypherAir arm64e integration branch to inspect. + required: true + default: carry/cypherair-arm64e-toolchain + type: string + upstream_ref: + description: Upstream Rust ref to merge in the dry-run workspace. + required: true + default: upstream/main + type: string + create_refresh_pr: + description: Push a refresh branch and open a draft PR when the dry-run merge succeeds. + required: true + default: false + type: boolean + dispatch_stage1_prerelease: + description: Dispatch arm64e stage1 prerelease for the refresh branch. + required: true + default: false + type: boolean + +permissions: + contents: write + pull-requests: write + actions: write + +jobs: + sync-prep: + if: github.repository == 'cypherair/rust' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Checkout fork + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Fetch upstream and integration refs + env: + INTEGRATION_BRANCH: ${{ inputs.integration_branch }} + run: | + set -euo pipefail + git remote add upstream https://github.com/rust-lang/rust.git 2>/dev/null || git remote set-url upstream https://github.com/rust-lang/rust.git + git fetch --prune origin \ + "+refs/heads/${INTEGRATION_BRANCH}:refs/remotes/origin/${INTEGRATION_BRANCH}" \ + +refs/heads/main:refs/remotes/origin/main + git fetch --prune upstream +refs/heads/main:refs/remotes/upstream/main + + - name: Report branch posture + env: + INTEGRATION_BRANCH: ${{ inputs.integration_branch }} + UPSTREAM_REF: ${{ inputs.upstream_ref }} + run: | + set -euo pipefail + { + echo "## arm64e upstream sync prep" + echo + echo "- integration branch: \`${INTEGRATION_BRANCH}\`" + echo "- upstream ref: \`${UPSTREAM_REF}\`" + echo "- origin/main: \`$(git rev-parse refs/remotes/origin/main)\`" + echo "- upstream/main: \`$(git rev-parse refs/remotes/upstream/main)\`" + echo "- integration head: \`$(git rev-parse refs/remotes/origin/${INTEGRATION_BRANCH})\`" + echo "- origin/main...upstream/main: \`$(git rev-list --left-right --count refs/remotes/origin/main...refs/remotes/upstream/main)\`" + echo "- integration...upstream: \`$(git rev-list --left-right --count refs/remotes/origin/${INTEGRATION_BRANCH}...${UPSTREAM_REF})\`" + } >> "$GITHUB_STEP_SUMMARY" + + - name: Dry-run merge upstream into integration branch + id: dry_run + env: + INTEGRATION_BRANCH: ${{ inputs.integration_branch }} + UPSTREAM_REF: ${{ inputs.upstream_ref }} + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -B arm64e-sync-dry-run "refs/remotes/origin/${INTEGRATION_BRANCH}" + if git merge --no-ff --no-commit "$UPSTREAM_REF"; then + echo "merge_ok=true" >> "$GITHUB_OUTPUT" + git diff --stat --cached || true + git merge --abort || true + else + echo "merge_ok=false" >> "$GITHUB_OUTPUT" + git status --short + exit 1 + fi + + - name: Create refresh branch and draft PR + id: refresh + if: ${{ inputs.create_refresh_pr && steps.dry_run.outputs.merge_ok == 'true' }} + env: + GITHUB_TOKEN: ${{ github.token }} + INTEGRATION_BRANCH: ${{ inputs.integration_branch }} + UPSTREAM_REF: ${{ inputs.upstream_ref }} + run: | + set -euo pipefail + SHORT_SHA="$(git rev-parse --short=7 "refs/remotes/origin/${INTEGRATION_BRANCH}")" + REFRESH_BRANCH="automation/arm64e-refresh-$(date -u +"%Y%m%dT%H%M%SZ")-${SHORT_SHA}" + + git checkout -B "$REFRESH_BRANCH" "refs/remotes/origin/${INTEGRATION_BRANCH}" + git merge --no-ff "$UPSTREAM_REF" -m "Merge ${UPSTREAM_REF} into ${INTEGRATION_BRANCH}" + git push origin "HEAD:refs/heads/${REFRESH_BRANCH}" + + gh pr create \ + --repo "$GITHUB_REPOSITORY" \ + --base "$INTEGRATION_BRANCH" \ + --head "$REFRESH_BRANCH" \ + --title "[automation] Refresh arm64e integration branch from ${UPSTREAM_REF}" \ + --body "Automated dry-run refresh branch for CypherAir arm64e Rust integration. This PR intentionally targets the integration branch and does not force-push it." \ + --draft + + echo "REFRESH_BRANCH=$REFRESH_BRANCH" >> "$GITHUB_ENV" + echo "branch=$REFRESH_BRANCH" >> "$GITHUB_OUTPUT" + + - name: Dispatch stage1 prerelease for refresh branch + if: ${{ inputs.dispatch_stage1_prerelease && inputs.create_refresh_pr && steps.refresh.outputs.branch != '' }} + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + gh workflow run arm64e-stage1-prerelease.yml \ + --repo "$GITHUB_REPOSITORY" \ + -f source_ref="${{ steps.refresh.outputs.branch }}" \ + -f release_tag_prefix=rust-arm64e-stage1-refresh \ + -f create_release=true diff --git a/.github/workflows/fork-arm64e.yml b/.github/workflows/fork-arm64e.yml new file mode 100644 index 0000000000000..76f1abd7f10f5 --- /dev/null +++ b/.github/workflows/fork-arm64e.yml @@ -0,0 +1,511 @@ +name: Fork arm64e validation + +# Fork-only CypherAir validation. Drop this workflow before proposing the +# upstreamable arm64e core to rust-lang/rust. + +on: + workflow_dispatch: + inputs: + validation_level: + description: Validation depth for manual fork runs. + required: true + default: targeted + type: choice + options: + - targeted + - extended + push: + branches: + - carry/cypherair-arm64e-toolchain + pull_request: + branches: + - main + schedule: + - cron: "43 11 * * 2" + +permissions: + contents: read + +concurrency: + group: fork-arm64e-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + arm64e: + if: github.repository == 'cypherair/rust' + strategy: + fail-fast: false + matrix: + include: + - runner_label: macos-15 + runner_name: macos-15 + - runner_label: macos-26 + runner_name: macos-26 + name: arm64e targeted validation (${{ matrix.runner_name }}) + runs-on: ${{ matrix.runner_label }} + timeout-minutes: 240 + defaults: + run: + shell: bash + steps: + - name: Checkout source + uses: actions/checkout@v5 + with: + fetch-depth: 2 + submodules: recursive + + # CypherAir fork push runs reproduced a shallow-clone case where bootstrap could not + # safely treat HEAD^1 as the upstream nightly base commit. Keep this in sync with src/stage0. + - name: Fetch nightly base ref + run: | + git fetch --no-tags --prune --depth=1 origin \ + +refs/heads/main:refs/remotes/origin/main + + - name: Show environment + run: | + mkdir -p /tmp/arm64e-diagnostics + { + echo "github.event_name=${GITHUB_EVENT_NAME}" + echo "runner_label=${{ matrix.runner_name }}" + sw_vers + uname -a + sysctl -n kern.osproductversion kern.osversion + sysctl -n machdep.cpu.brand_string 2>/dev/null || true + git show -s --format='HEAD: %H %P %ae %s' HEAD + git show -s --format='origin/main: %H %P %ae %s' refs/remotes/origin/main + python3 --version + clang --version + rustc -vV || true + } | tee /tmp/arm64e-diagnostics/runner-fingerprint.txt + + - name: Check arm64e codegen crates + id: check_codegen + run: | + python3 x.py check compiler/rustc_codegen_ssa compiler/rustc_codegen_llvm --stage 1 + + - name: Run targeted arm64e tests + id: targeted_tests + run: | + python3 x.py test --stage 1 --force-rerun \ + tests/codegen-llvm/arm64e-apple-ptrauth*.rs \ + tests/assembly-llvm/arm64e-apple-ptrauth-calls.rs \ + tests/assembly-llvm/targets/targets-macho.rs \ + tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.rs \ + tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.rs + + - name: Build stage1 arm64e std toolchain + id: build_stage1 + run: | + python3 x.py build compiler/rustc library/std --stage 1 --target arm64e-apple-darwin + + - name: Prepare arm64e smoke helpers + run: | + mkdir -p /tmp/arm64e-diagnostics + cat > /tmp/arm64e_collect_smoke.py <<'PY' + import argparse + import json + import pathlib + import re + import subprocess + import sys + + + def run_capture(cmd, timeout=None): + proc = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) + return { + "cmd": cmd, + "returncode": proc.returncode, + "signal": -proc.returncode if proc.returncode < 0 else None, + "stdout": proc.stdout, + "stderr": proc.stderr, + } + + + def load_text(path): + try: + return pathlib.Path(path).read_text() + except FileNotFoundError: + return None + + + parser = argparse.ArgumentParser() + parser.add_argument("--name", required=True) + parser.add_argument("--rustc", required=True) + parser.add_argument("--source", required=True) + parser.add_argument("--output-base", required=True) + parser.add_argument("--json-out", required=True) + parser.add_argument("--target", default="arm64e-apple-darwin") + parser.add_argument("--emit", default="link") + parser.add_argument("--run-binary", action="store_true") + parser.add_argument("--expected-stdout") + parser.add_argument("--expected-stdout-regex") + parser.add_argument("--asm-pattern") + args = parser.parse_args() + + output_base = pathlib.Path(args.output_base) + compile_cmd = [ + args.rustc, + args.source, + "--target", + args.target, + "-O", + "--emit", + args.emit, + "-o", + str(output_base), + ] + payload = { + "name": args.name, + "target": args.target, + "emit": args.emit, + "compile": run_capture(compile_cmd), + "output_base": str(output_base), + "paths": { + "source": args.source, + "binary": str(output_base), + "asm": str(output_base.with_suffix(".s")), + "ir": str(output_base.with_suffix(".ll")), + }, + } + + overall_ok = payload["compile"]["returncode"] == 0 + + if overall_ok: + payload["file"] = run_capture(["/usr/bin/file", str(output_base)]) + else: + payload["file"] = None + + asm_path = output_base.with_suffix(".s") + asm_text = load_text(asm_path) + if asm_text is not None and args.asm_pattern: + payload["asm_matches"] = re.findall(args.asm_pattern, asm_text) + else: + payload["asm_matches"] = [] + + if args.run_binary and overall_ok: + payload["run"] = run_capture([str(output_base)], timeout=30) + if payload["run"]["returncode"] != 0: + overall_ok = False + stdout = payload["run"]["stdout"] + if args.expected_stdout is not None: + if stdout.rstrip("\n") != args.expected_stdout.rstrip("\n"): + payload["stdout_mismatch"] = { + "expected": args.expected_stdout, + "actual": stdout, + } + overall_ok = False + if args.expected_stdout_regex is not None: + if re.fullmatch(args.expected_stdout_regex, stdout.strip()) is None: + payload["stdout_regex_mismatch"] = { + "expected_regex": args.expected_stdout_regex, + "actual": stdout, + } + overall_ok = False + else: + payload["run"] = None + + json_path = pathlib.Path(args.json_out) + json_path.write_text(json.dumps(payload, indent=2, sort_keys=True)) + + print(f"== {args.name} compile ==") + print(f"returncode={payload['compile']['returncode']}") + if payload["compile"]["stdout"]: + print("-- stdout --") + print(payload["compile"]["stdout"], end="") + if payload["compile"]["stderr"]: + print("-- stderr --") + print(payload["compile"]["stderr"], end="") + if payload["file"] is not None: + print("== file ==") + print(payload["file"]["stdout"], end="") + if payload["run"] is not None: + print("== run ==") + print(f"returncode={payload['run']['returncode']}") + if payload["run"]["signal"] is not None: + print(f"signal={payload['run']['signal']}") + print("-- stdout --") + print(payload["run"]["stdout"], end="") + print("-- stderr --") + print(payload["run"]["stderr"], end="") + if payload["asm_matches"]: + print("== asm matches ==") + for match in payload["asm_matches"]: + print(match) + + sys.exit(0 if overall_ok else 1) + PY + + - name: Diagnose arm64e hello runtime + id: hello_diag + continue-on-error: true + run: | + HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')" + STAGE1="build/${HOST_TRIPLE}/stage1/bin/rustc" + + cat > /tmp/arm64e-diagnostics/arm64e_hello.rs <<'EOF' + fn main() { + println!("hello-stage1"); + } + EOF + + python3 /tmp/arm64e_collect_smoke.py \ + --name hello \ + --rustc "$STAGE1" \ + --source /tmp/arm64e-diagnostics/arm64e_hello.rs \ + --output-base /tmp/arm64e-diagnostics/arm64e_hello \ + --json-out /tmp/arm64e-diagnostics/arm64e_hello.json \ + --emit link \ + --run-binary \ + --expected-stdout 'hello-stage1' + + - name: Diagnose arm64e thread id runtime + id: thread_id_diag + continue-on-error: true + run: | + HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')" + STAGE1="build/${HOST_TRIPLE}/stage1/bin/rustc" + + cat > /tmp/arm64e-diagnostics/arm64e_thread_id.rs <<'EOF' + fn main() { + println!("{:?}", std::thread::current().id()); + } + EOF + + python3 /tmp/arm64e_collect_smoke.py \ + --name thread_id \ + --rustc "$STAGE1" \ + --source /tmp/arm64e-diagnostics/arm64e_thread_id.rs \ + --output-base /tmp/arm64e-diagnostics/arm64e_thread_id \ + --json-out /tmp/arm64e-diagnostics/arm64e_thread_id.json \ + --emit link \ + --run-binary \ + --expected-stdout-regex '^ThreadId\([0-9]+\)$' + + - name: Diagnose arm64e function pointer runtime + id: fnptr_diag + continue-on-error: true + run: | + HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')" + STAGE1="build/${HOST_TRIPLE}/stage1/bin/rustc" + + cat > /tmp/arm64e-diagnostics/arm64e_fnptr.rs <<'EOF' + #[inline(never)] + pub extern "C" fn target(x: i32) -> i32 { x + 1 } + + fn main() { + let f: extern "C" fn(i32) -> i32 = target; + println!("{}", f(41)); + } + EOF + + python3 /tmp/arm64e_collect_smoke.py \ + --name fnptr \ + --rustc "$STAGE1" \ + --source /tmp/arm64e-diagnostics/arm64e_fnptr.rs \ + --output-base /tmp/arm64e-diagnostics/arm64e_fnptr \ + --json-out /tmp/arm64e-diagnostics/arm64e_fnptr.json \ + --emit llvm-ir,asm,link \ + --run-binary \ + --expected-stdout '42' \ + --asm-pattern 'braaz|blraaz|blr' + + echo "LLVM IR excerpt:" + sed -n '1,40p' /tmp/arm64e-diagnostics/arm64e_fnptr.ll || true + echo + echo "Assembly excerpt:" + sed -n '1,40p' /tmp/arm64e-diagnostics/arm64e_fnptr.s || true + + - name: Diagnose arm64e TLS runtime + id: tls_diag + continue-on-error: true + run: | + HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')" + STAGE1="build/${HOST_TRIPLE}/stage1/bin/rustc" + + cat > /tmp/arm64e-diagnostics/arm64e_tls_smoke.rs <<'EOF' + use std::cell::RefCell; + + fn main() { + thread_local! { + static S: RefCell = RefCell::default(); + } + + S.with(|x| *x.borrow_mut() = "pika pika".to_string()); + S.with(|x| println!("{}", x.borrow())); + } + EOF + + python3 /tmp/arm64e_collect_smoke.py \ + --name tls \ + --rustc "$STAGE1" \ + --source /tmp/arm64e-diagnostics/arm64e_tls_smoke.rs \ + --output-base /tmp/arm64e-diagnostics/arm64e_tls_smoke \ + --json-out /tmp/arm64e-diagnostics/arm64e_tls_smoke.json \ + --emit llvm-ir,asm,link \ + --run-binary \ + --expected-stdout 'pika pika' \ + --asm-pattern 'braaz|blraaz|blr' + + echo "LLVM IR excerpt:" + sed -n '1,40p' /tmp/arm64e-diagnostics/arm64e_tls_smoke.ll || true + echo + echo "Assembly excerpt:" + sed -n '1,60p' /tmp/arm64e-diagnostics/arm64e_tls_smoke.s || true + + - name: Upload arm64e diagnostics + if: always() + uses: actions/upload-artifact@v4 + with: + name: arm64e-diagnostics-${{ matrix.runner_name }} + if-no-files-found: ignore + path: /tmp/arm64e-diagnostics + + - name: Summarize arm64e validation + if: always() + run: | + python3 - <<'PY' >> "$GITHUB_STEP_SUMMARY" + import json + import pathlib + + root = pathlib.Path("/tmp/arm64e-diagnostics") + cases = [ + ("hello", "arm64e_hello.json"), + ("thread_id", "arm64e_thread_id.json"), + ("fnptr", "arm64e_fnptr.json"), + ("tls", "arm64e_tls_smoke.json"), + ] + + def sanitize(text): + if text is None: + return "" + text = text.strip() + if not text: + return "" + return text.replace("|", "\\|").replace("\n", "
") + + print("## arm64e validation (${{ matrix.runner_name }})") + print() + print("- check_codegen: ${{ steps.check_codegen.outcome }}") + print("- targeted_tests: ${{ steps.targeted_tests.outcome }}") + print("- build_stage1: ${{ steps.build_stage1.outcome }}") + print("- hello_diag (non-blocking): ${{ steps.hello_diag.outcome }}") + print("- thread_id_diag (non-blocking): ${{ steps.thread_id_diag.outcome }}") + print("- fnptr_diag (non-blocking): ${{ steps.fnptr_diag.outcome }}") + print("- tls_diag (non-blocking): ${{ steps.tls_diag.outcome }}") + print() + print("| case | compile | run | rc/signal | file | stdout | asm hits |") + print("| --- | --- | --- | --- | --- | --- | --- |") + for case_name, json_name in cases: + path = root / json_name + if not path.exists(): + print(f"| {case_name} | missing | missing | | | | |") + continue + data = json.loads(path.read_text()) + compile_status = "ok" if data["compile"]["returncode"] == 0 else "fail" + run = data.get("run") + if run is None: + run_status = "not-run" + rc_signal = "" + stdout = "" + else: + run_status = "ok" if run["returncode"] == 0 else "fail" + rc_signal = f"rc={run['returncode']}" + if run.get("signal") is not None: + rc_signal += f" sig={run['signal']}" + stdout = sanitize(run.get("stdout")) + file_output = sanitize((data.get("file") or {}).get("stdout")) + asm_hits = sanitize(",".join(data.get("asm_matches") or [])) + print( + f"| {case_name} | {compile_status} | {run_status} | {sanitize(rc_signal)} | " + f"{file_output} | {stdout} | {asm_hits} |" + ) + print() + print("The four host-binary smoke steps are non-blocking diagnostics for fork runner coverage; targeted tests and stage1 build remain blocking.") + PY + + { + echo "Smoke diagnostic JSON files:" + ls -1 /tmp/arm64e-diagnostics/*.json + } + + arm64e-extended: + if: ${{ github.repository == 'cypherair/rust' && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.validation_level == 'extended')) }} + strategy: + fail-fast: false + matrix: + runner_label: + - macos-15 + - macos-26 + suite: + - check-codegen + - codegen-ptrauth + - assembly-ptrauth + - targets-macho + - ui-ptrauth-flags + - stage1-std + name: arm64e extended (${{ matrix.suite }}, ${{ matrix.runner_label }}) + runs-on: ${{ matrix.runner_label }} + timeout-minutes: 360 + defaults: + run: + shell: bash + steps: + - name: Checkout source + uses: actions/checkout@v5 + with: + fetch-depth: 2 + submodules: recursive + + - name: Fetch nightly base ref + run: | + git fetch --no-tags --prune --depth=1 origin \ + +refs/heads/main:refs/remotes/origin/main + + - name: Show environment + run: | + { + echo "github.event_name=${GITHUB_EVENT_NAME}" + echo "validation_level=${{ inputs.validation_level || 'scheduled' }}" + echo "runner_label=${{ matrix.runner_label }}" + echo "suite=${{ matrix.suite }}" + sw_vers + uname -a + git show -s --format='HEAD: %H %P %ae %s' HEAD + git show -s --format='origin/main: %H %P %ae %s' refs/remotes/origin/main + python3 --version + clang --version + rustc -vV || true + } + + - name: Run extended suite + run: | + set -euo pipefail + case "${{ matrix.suite }}" in + check-codegen) + python3 x.py check compiler/rustc_codegen_ssa compiler/rustc_codegen_llvm --stage 1 + ;; + codegen-ptrauth) + python3 x.py test --stage 1 --force-rerun \ + tests/codegen-llvm/arm64e-apple-ptrauth*.rs + ;; + assembly-ptrauth) + python3 x.py test --stage 1 --force-rerun \ + tests/assembly-llvm/arm64e-apple-ptrauth*.rs + ;; + targets-macho) + python3 x.py test --stage 1 --force-rerun \ + tests/assembly-llvm/targets/targets-macho.rs + ;; + ui-ptrauth-flags) + python3 x.py test --stage 1 --force-rerun \ + tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.rs \ + tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.rs + ;; + stage1-std) + python3 x.py build compiler/rustc library/std --stage 1 --target arm64e-apple-darwin + ;; + *) + echo "::error::unknown extended suite: ${{ matrix.suite }}" + exit 1 + ;; + esac diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 1f59d250e08a0..8f1f80354771c 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -578,6 +578,8 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } + to_add.extend(default_arm64e_ptrauth_attrs(cx, sess)); + let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones @@ -613,3 +615,32 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> { tcx.wasm_import_module_map(id.krate).get(&id) } + +pub(crate) fn has_default_arm64e_ptrauth(sess: &Session) -> bool { + sess.target.is_apple_arm64e() + && sess.unstable_target_features.contains(&sym::paca) + && sess.unstable_target_features.contains(&sym::pacg) +} + +pub(crate) fn default_arm64e_ptrauth_attrs<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, +) -> SmallVec<[&'ll Attribute; 4]> { + let mut attrs = SmallVec::new(); + if has_default_arm64e_ptrauth(sess) { + attrs.push(llvm::CreateAttrString(cx.llcx, "ptrauth-auth-traps")); + attrs.push(llvm::CreateAttrString(cx.llcx, "ptrauth-calls")); + attrs.push(llvm::CreateAttrString(cx.llcx, "ptrauth-indirect-gotos")); + attrs.push(llvm::CreateAttrString(cx.llcx, "ptrauth-returns")); + } + attrs +} + +pub(crate) fn apply_default_arm64e_ptrauth_attrs<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, + llfn: &'ll Value, +) { + let attrs = default_arm64e_ptrauth_attrs(cx, sess); + attributes::apply_to_llfn(llfn, Function, &attrs); +} diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 417f321a6b4a7..dcc36d6f26b88 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -60,6 +60,7 @@ fn write_output_file<'ll>( file_type: llvm::FileType, self_profiler_ref: &SelfProfilerRef, verify_llvm_ir: bool, + strip_unsupported_ptrauth_bundles: bool, ) { debug!("write_output_file output={:?} dwo_output={:?}", output, dwo_output); let output_c = path_to_c_string(output); @@ -70,6 +71,14 @@ fn write_output_file<'ll>( } else { std::ptr::null() }; + + if strip_unsupported_ptrauth_bundles { + // LLVM may devirtualize indirect arm64e calls into direct calls after we + // attached ptrauth operand bundles. Strip bundles from call shapes LLVM + // cannot lower just before object/assembly emission. + llvm::strip_unsupported_ptrauth_bundles(m); + } + let result = unsafe { let pm = llvm::LLVMCreatePassManager(); llvm::LLVMAddAnalysisPasses(target, pm); @@ -734,8 +743,12 @@ pub(crate) unsafe fn llvm_optimize( } if cgcx.target_is_like_gpu && config.offload.contains(&config::Offload::Device) { - let cx = - SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size); + let cx = SimpleCx::new( + module.module_llvm.llmod(), + module.module_llvm.llcx, + cgcx.pointer_size, + false, + ); for func in cx.get_functions() { let offload_kernel = "offload-kernel"; if attributes::has_string_attr(func, offload_kernel) { @@ -867,6 +880,7 @@ pub(crate) unsafe fn llvm_optimize( llvm::FileType::ObjectFile, prof, true, + cgcx.target_is_apple_arm64e, ); // We ignore cgcx.save_temps here and unconditionally always keep our `host.out` artifact. // Otherwise, recompiling the host code would fail since we deleted that device artifact @@ -1040,6 +1054,10 @@ pub(crate) fn codegen( cgcx.output_filenames.temp_path_for_cgu(OutputType::LlvmAssembly, &module.name); let out_c = path_to_c_string(&out); + if cgcx.target_is_apple_arm64e { + llvm::strip_unsupported_ptrauth_bundles(llmod); + } + extern "C" fn demangle_callback( input_ptr: *const c_char, input_len: size_t, @@ -1102,6 +1120,7 @@ pub(crate) fn codegen( llvm::FileType::AssemblyFile, prof, config.verify_llvm_ir, + cgcx.target_is_apple_arm64e, ); } @@ -1135,6 +1154,7 @@ pub(crate) fn codegen( llvm::FileType::ObjectFile, prof, config.verify_llvm_ir, + cgcx.target_is_apple_arm64e, ); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7e5f71209fbab..b3655da885d96 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -68,10 +68,14 @@ impl<'a, 'll> SBuilder<'a, 'll> { let args = self.check_call("call", llty, llfn, args); let funclet_bundle = funclet.map(|funclet| funclet.bundle()); - let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + let mut bundles: SmallVec<[_; 3]> = SmallVec::new(); if let Some(funclet_bundle) = funclet_bundle { bundles.push(funclet_bundle); } + let ptrauth_bundle = self.ptrauth_operand_bundle(llfn); + if let Some(ptrauth_bundle) = ptrauth_bundle.as_ref().map(|b| b.as_ref()) { + bundles.push(ptrauth_bundle); + } let call = unsafe { llvm::LLVMBuildCallWithOperandBundles( @@ -187,6 +191,18 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { load } } + + fn ptrauth_operand_bundle(&mut self, llfn: &'ll Value) -> Option> { + let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; + if self.cx.is_apple_arm64e() && is_indirect_call { + Some(llvm::OperandBundleBox::new( + "ptrauth", + &[self.cx.get_const_i32(0), self.cx.get_const_i64(0)], + )) + } else { + None + } + } } /// Empty string, to be used where LLVM expects an instruction name, indicating @@ -415,10 +431,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let args = self.check_call("invoke", llty, llfn, args); let funclet_bundle = funclet.map(|funclet| funclet.bundle()); - let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + let mut bundles: SmallVec<[_; 3]> = SmallVec::new(); if let Some(funclet_bundle) = funclet_bundle { bundles.push(funclet_bundle); } + let ptrauth_bundle = self.ptrauth_operand_bundle(llfn); + if let Some(ptrauth_bundle) = ptrauth_bundle.as_ref().map(|b| b.as_ref()) { + bundles.push(ptrauth_bundle); + } // Emit CFI pointer type membership test self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); @@ -1392,10 +1412,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let args = self.check_call("call", llty, llfn, args); let funclet_bundle = funclet.map(|funclet| funclet.bundle()); - let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + let mut bundles: SmallVec<[_; 3]> = SmallVec::new(); if let Some(funclet_bundle) = funclet_bundle { bundles.push(funclet_bundle); } + let ptrauth_bundle = self.ptrauth_operand_bundle(llfn); + if let Some(ptrauth_bundle) = ptrauth_bundle.as_ref().map(|b| b.as_ref()) { + bundles.push(ptrauth_bundle); + } // Emit CFI pointer type membership test self.cfi_type_test(caller_attrs, fn_abi, callee_instance, llfn); @@ -1833,7 +1857,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let args = self.check_call("callbr", llty, llfn, args); let funclet_bundle = funclet.map(|funclet| funclet.bundle()); - let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + let mut bundles: SmallVec<[_; 3]> = SmallVec::new(); if let Some(funclet_bundle) = funclet_bundle { bundles.push(funclet_bundle); } @@ -1848,6 +1872,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } let callbr = unsafe { + // LLVM cannot lower arm64e ptrauth operand bundles on callbr yet. + // Keep the ordinary call/invoke arm64e coverage, but leave asm-goto + // style control flow as a follow-up. llvm::LLVMBuildCallBr( self.llbuilder, llty, diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 5c5e9ed7e082c..e3462ea3ea52f 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -319,7 +319,9 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { value } } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), + GlobalAlloc::Function { instance, .. } => { + self.arm64e_fn_ptr_for_data(self.get_fn_addr(instance)) + } GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 3514fb145612a..52a7b331ac4c6 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -229,6 +229,24 @@ impl<'ll> CodegenCx<'ll, '_> { unsafe { llvm::LLVMConstPointerCast(val, ty) } } + pub(crate) fn arm64e_fn_ptr_for_data(&self, fn_addr: &'ll Value) -> &'ll Value { + if !self.is_apple_arm64e() { + return fn_addr; + } + + let fn_addr = self.const_pointercast(fn_addr, self.type_ptr()); + unsafe { + // ConstantPtrAuth requires an i64 discriminator and a pointer-typed + // address discriminator, even when both are effectively "zero". + llvm::LLVMConstantPtrAuth( + fn_addr, + self.get_const_i32(0), + self.get_const_i64(0), + self.const_null(self.type_ptr()), + ) + } + } + /// Create a global variable. /// /// The returned global variable is a pointer in the default address space for globals. diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 5b730b820b84a..8eb5db17b2934 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -48,6 +48,7 @@ pub(crate) struct SCx<'ll> { pub llmod: &'ll llvm::Module, pub llcx: &'ll llvm::Context, pub isize_ty: &'ll Type, + pub is_apple_arm64e: bool, } impl<'ll> Borrow> for FullCx<'ll, '_> { @@ -185,7 +186,12 @@ pub(crate) unsafe fn create_module<'ll>( let mod_name = SmallCStr::new(mod_name); let llmod = unsafe { llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx) }; - let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()); + let cx = SimpleCx::new( + llmod, + llcx, + tcx.data_layout.pointer_size(), + attributes::has_default_arm64e_ptrauth(sess), + ); let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); @@ -426,6 +432,39 @@ pub(crate) unsafe fn create_module<'ll>( } } + if attributes::has_default_arm64e_ptrauth(sess) { + let ptrauth_abi_version = unsafe { + llvm::LLVMMDNodeInContext2( + llcx, + [llvm::LLVMMDNodeInContext2( + llcx, + [ + llvm::LLVMValueAsMetadata(llvm::LLVMConstInt( + llvm::LLVMInt32TypeInContext(llcx), + 0, + llvm::FALSE, + )), + llvm::LLVMValueAsMetadata(llvm::LLVMConstInt( + llvm::LLVMInt1TypeInContext(llcx), + 0, + llvm::FALSE, + )), + ] + .as_ptr(), + 2, + )] + .as_ptr(), + 1, + ) + }; + llvm::add_module_flag_metadata( + llmod, + llvm::ModuleFlagMergeBehavior::AppendUnique, + "ptrauth.abi-version", + ptrauth_abi_version, + ); + } + // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection { llvm::add_module_flag_u32( @@ -621,7 +660,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { GenericCx( FullCx { tcx, - scx: SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()), + scx: SimpleCx::new( + llmod, + llcx, + tcx.data_layout.pointer_size(), + attributes::has_default_arm64e_ptrauth(tcx.sess), + ), use_dll_storage_attrs, tls_model, codegen_unit, @@ -749,13 +793,18 @@ impl<'ll> SimpleCx<'ll> { llmod: &'ll llvm::Module, llcx: &'ll llvm::Context, pointer_size: Size, + is_apple_arm64e: bool, ) -> Self { let isize_ty = llvm::LLVMIntTypeInContext(llcx, pointer_size.bits() as c_uint); - Self(SCx { llmod, llcx, isize_ty }, PhantomData) + Self(SCx { llmod, llcx, isize_ty, is_apple_arm64e }, PhantomData) } } impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { + pub(crate) fn is_apple_arm64e(&self) -> bool { + (**self).borrow().is_apple_arm64e + } + pub(crate) fn get_metadata_value(&self, metadata: &'ll Metadata) -> &'ll Value { llvm::LLVMMetadataAsValue(self.llcx(), metadata) } @@ -836,6 +885,10 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { get_fn(self, instance) } + fn get_fn_addr_for_data(&self, instance: Instance<'tcx>) -> &'ll Value { + self.arm64e_fn_ptr_for_data(get_fn(self, instance)) + } + fn eh_personality(&self) -> &'ll Value { // The exception handling personality function. // @@ -930,6 +983,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { llvm::AttributePlace::Function, attributes::target_features_attr(self, self.tcx, vec![]).as_slice(), ); + attributes::apply_default_arm64e_ptrauth_attrs(self, self.sess(), llfn); Some(llfn) } else { // If the symbol already exists, it is an error: for example, the user wrote diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 9b7744cc81c10..56a98ead1e4fb 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -32,6 +32,7 @@ use rustc_target::spec::{Arch, Os}; use tracing::debug; use crate::abi::FnAbiLlvmExt; +use crate::attributes; use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::builder::gpu_offload::{ @@ -1721,6 +1722,7 @@ fn gen_fn<'a, 'll, 'tcx>( let llfn = cx.declare_fn(name, fn_abi, None); cx.set_frame_pointer_type(llfn); cx.apply_target_cpu_attr(llfn); + attributes::apply_default_arm64e_ptrauth_attrs(cx, cx.tcx.sess, llfn); // FIXME(eddyb) find a nicer way to do this. llvm::set_linkage(llfn, llvm::Linkage::InternalLinkage); let llbb = Builder::append_block(cx, llfn, "entry-block"); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index e4312ff935c04..1825cd1163e95 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -100,8 +100,12 @@ impl ExtraBackendMethods for LlvmCodegenBackend { methods: &[AllocatorMethod], ) -> ModuleLlvm { let module_llvm = ModuleLlvm::new_metadata(tcx, module_name); - let cx = - SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size()); + let cx = SimpleCx::new( + module_llvm.llmod(), + &module_llvm.llcx, + tcx.data_layout.pointer_size(), + attributes::has_default_arm64e_ptrauth(tcx.sess), + ); unsafe { allocator::codegen(tcx, cx, module_name, methods); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9ecf5afcbcb35..cee924642475b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1023,6 +1023,12 @@ unsafe extern "C" { ConstantVals: *const &'a Value, Count: c_uint, ) -> &'a Value; + pub(crate) fn LLVMConstantPtrAuth<'a>( + Ptr: &'a Value, + Key: &'a Value, + Disc: &'a Value, + AddrDisc: &'a Value, + ) -> &'a Value; pub(crate) fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; // Constant expressions @@ -2192,6 +2198,14 @@ unsafe extern "C" { ValueLen: size_t, ); + pub(crate) fn LLVMRustAddModuleFlagMetadata( + M: &Module, + MergeBehavior: ModuleFlagMergeBehavior, + Name: *const c_char, + NameLen: size_t, + Value: &Metadata, + ); + pub(crate) fn LLVMRustDIBuilderCreateCompileUnit<'a>( Builder: &DIBuilder<'a>, Lang: c_uint, @@ -2502,6 +2516,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustBufferFree(p: &'static mut Buffer); pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; pub(crate) fn LLVMRustModuleInstructionStats(M: &Module) -> u64; + pub(crate) fn LLVMRustStripUnsupportedPtrauthBundles(M: &Module); pub(crate) fn LLVMRustModuleSerialize(M: &Module, is_thin: bool) -> &'static mut Buffer; pub(crate) fn LLVMRustCreateThinLTOData( diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2ec19b1795b5a..9d57d291de363 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -445,6 +445,29 @@ pub(crate) fn add_module_flag_str( } } +pub(crate) fn add_module_flag_metadata( + module: &Module, + merge_behavior: ModuleFlagMergeBehavior, + key: &str, + value: &Metadata, +) { + unsafe { + LLVMRustAddModuleFlagMetadata( + module, + merge_behavior, + key.as_c_char_ptr(), + key.len(), + value, + ); + } +} + +pub(crate) fn strip_unsupported_ptrauth_bundles(module: &Module) { + unsafe { + LLVMRustStripUnsupportedPtrauthBundles(module); + } +} + pub(crate) fn set_dllimport_storage_class<'ll>(v: &'ll Value) { unsafe { LLVMSetDLLStorageClass(v, DLLStorageClass::DllImport); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index f3e28484bf11d..099eafa872fe4 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -447,7 +447,7 @@ fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachO /// Is Apple's CPU subtype `arm64e`s fn macho_is_arm64e(target: &Target) -> bool { - target.llvm_target.starts_with("arm64e") + target.is_apple_arm64e() } pub(crate) enum MetadataPosition { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7b22ac231df1c..975000f498c91 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -335,6 +335,7 @@ pub struct CodegenContext { pub is_pe_coff: bool, pub target_can_use_split_dwarf: bool, pub target_arch: String, + pub target_is_apple_arm64e: bool, pub target_is_like_darwin: bool, pub target_is_like_aix: bool, pub target_is_like_gpu: bool, @@ -1272,6 +1273,7 @@ fn start_executing_work( is_pe_coff: tcx.sess.target.is_like_windows, target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(), target_arch: tcx.sess.target.arch.to_string(), + target_is_apple_arm64e: tcx.sess.target.is_apple_arm64e(), target_is_like_darwin: tcx.sess.target.is_like_darwin, target_is_like_aix: tcx.sess.target.is_like_aix, target_is_like_gpu: tcx.sess.target.is_like_gpu, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 473a73d43a93a..fbb4df8952352 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -492,7 +492,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return None; } - let main_llfn = cx.get_fn_addr(instance); + let main_llfn = cx.get_fn_addr_for_data(instance); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index cceda09c701cc..a1d3add23718a 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1230,6 +1230,12 @@ pub(crate) struct ForbiddenCTargetFeature<'a> { pub reason: &'a str, } +#[derive(Diagnostic)] +#[diag( + "`-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default" +)] +pub(crate) struct Arm64eApplePtrauthDisableForbidden; + pub(crate) struct TargetFeatureDisableOrEnable<'a> { pub features: &'a [&'a str], pub span: Option, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 45c6621ba0131..2f71d828eedbe 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -423,7 +423,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ) .unwrap(); - OperandValue::Immediate(bx.get_fn_addr(instance)) + OperandValue::Immediate(bx.get_fn_addr_for_data(instance)) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -437,7 +437,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ty::ClosureKind::FnOnce, ); - OperandValue::Immediate(bx.cx().get_fn_addr(instance)) + OperandValue::Immediate(bx.cx().get_fn_addr_for_data(instance)) } _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 2572d21608213..a039b3111b52a 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -213,6 +213,14 @@ fn parse_rust_feature_list<'a>( } } +fn is_forbidden_arm64e_apple_ptrauth_disable( + sess: &Session, + base_feature: &str, + enable: bool, +) -> bool { + !enable && sess.target.is_apple_arm64e() && matches!(base_feature, "paca" | "pacg" | "pauth") +} + /// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically, /// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and /// 2nd component of the return value, respectively). @@ -253,6 +261,7 @@ pub fn cfg_target_feature<'a, const N: usize>( .collect(); let mut enabled_disabled_features = FxHashMap::default(); + let mut arm64e_apple_ptrauth_disable_forbidden = false; // Add enabled and remove disabled features. parse_rust_feature_list( @@ -263,6 +272,14 @@ pub fn cfg_target_feature<'a, const N: usize>( sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature }); }, |base_feature, new_features, enable| { + if is_forbidden_arm64e_apple_ptrauth_disable(sess, base_feature, enable) { + if !arm64e_apple_ptrauth_disable_forbidden { + sess.dcx().emit_err(errors::Arm64eApplePtrauthDisableForbidden); + arm64e_apple_ptrauth_disable_forbidden = true; + } + return; + } + // Iteration order is irrelevant since this only influences an `FxHashMap`. #[allow(rustc::potential_query_instability)] enabled_disabled_features.extend(new_features.iter().map(|&s| (s, enable))); @@ -446,7 +463,11 @@ pub fn flag_to_backend_features<'a>( |_feature| { // Errors are already emitted in `cfg_target_feature`; avoid duplicates. }, - |_base_feature, new_features, enable| { + |base_feature, new_features, enable| { + if is_forbidden_arm64e_apple_ptrauth_disable(sess, base_feature, enable) { + return; + } + rust_features.extend( UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)), ); diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 6a0f889833492..0a06cdfe72bee 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -19,6 +19,9 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { } fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function; fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value; + fn get_fn_addr_for_data(&self, instance: Instance<'tcx>) -> Self::Value { + self.get_fn_addr(instance) + } fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; fn set_frame_pointer_type(&self, llfn: Self::Function); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index b03a07538ccd8..df2560d1ec27e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1,4 +1,5 @@ #include "LLVMWrapper.h" +#include "PtrauthUtils.h" #include "llvm-c/Core.h" #include "llvm/ADT/ArrayRef.h" @@ -836,6 +837,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( ModulePassManager MPM; bool NeedThinLTOBufferPasses = true; + bool StripUnsupportedPtrauthBundles = + rustc_llvm::isAppleArm64eModule(*TheModule); auto ThinLTOBuffer = std::make_unique(); auto ThinLTOSummaryBuffer = std::make_unique(); raw_string_ostream ThinLTODataOS(ThinLTOBuffer->data); @@ -863,6 +866,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( // bitcode for embedding is obtained after performing // `ThinLTOPreLinkDefaultPipeline`. MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(OptLevel)); + rustc_llvm::addStripUnsupportedPtrauthBundlesPass( + MPM, StripUnsupportedPtrauthBundles); MPM.addPass(ThinLTOBitcodeWriterPass( ThinLTODataOS, ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr)); @@ -920,6 +925,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( if (ThinLTOBufferRef && *ThinLTOBufferRef == nullptr) { // thin lto summaries prevent fat lto, so do not emit them if fat // lto is requested. See PR #136840 for background information. + rustc_llvm::addStripUnsupportedPtrauthBundlesPass( + MPM, StripUnsupportedPtrauthBundles); if (OptStage != LLVMRustOptStage::PreLinkFatLTO) { MPM.addPass(ThinLTOBitcodeWriterPass( ThinLTODataOS, ThinLTOSummaryBufferRef ? &ThinLinkDataOS : nullptr)); @@ -1438,6 +1445,8 @@ extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, bool is_thin) { + bool StripUnsupportedPtrauthBundles = + rustc_llvm::isAppleArm64eModule(*unwrap(M)); auto Ret = std::make_unique(); { auto OS = raw_string_ostream(Ret->data); @@ -1454,9 +1463,13 @@ extern "C" LLVMRustBuffer *LLVMRustModuleSerialize(LLVMModuleRef M, PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; + rustc_llvm::addStripUnsupportedPtrauthBundlesPass( + MPM, StripUnsupportedPtrauthBundles); MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr)); MPM.run(*unwrap(M), MAM); } else { + if (StripUnsupportedPtrauthBundles) + rustc_llvm::stripUnsupportedPtrauthBundles(*unwrap(M)); WriteBitcodeToFile(*unwrap(M), OS); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/PtrauthUtils.h b/compiler/rustc_llvm/llvm-wrapper/PtrauthUtils.h new file mode 100644 index 0000000000000..18df4e11a0619 --- /dev/null +++ b/compiler/rustc_llvm/llvm-wrapper/PtrauthUtils.h @@ -0,0 +1,66 @@ +#ifndef INCLUDED_RUSTC_LLVM_PTRAUTHUTILS_H +#define INCLUDED_RUSTC_LLVM_PTRAUTHUTILS_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" + +namespace rustc_llvm { + +inline bool isAppleArm64eModule(llvm::Module &Mod) { + return llvm::StringRef(Mod.getTargetTriple().str()).starts_with("arm64e-apple-"); +} + +inline bool stripUnsupportedPtrauthBundles(llvm::Module &Mod) { + llvm::LLVMContext &Ctx = Mod.getContext(); + const uint32_t PtrauthID = Ctx.getOperandBundleTagID("ptrauth"); + llvm::SmallVector ToRewrite; + + for (llvm::Function &F : Mod) { + for (llvm::BasicBlock &BB : F) { + for (llvm::Instruction &I : BB) { + auto *CB = llvm::dyn_cast(&I); + if (!CB || !CB->countOperandBundlesOfType(PtrauthID)) + continue; + + // LLVM still does not lower ptrauth bundles on callbr. Direct-call + // bundles should be prevented by LLVM canonicalization instead of + // repaired here before serialization. + if (llvm::isa(CB)) + ToRewrite.push_back(CB); + } + } + } + + for (llvm::CallBase *CB : ToRewrite) { + llvm::CallBase *Replacement = + llvm::CallBase::removeOperandBundle(CB, PtrauthID, CB->getIterator()); + Replacement->copyMetadata(*CB); + Replacement->setDebugLoc(CB->getDebugLoc()); + CB->replaceAllUsesWith(Replacement); + CB->eraseFromParent(); + } + + return !ToRewrite.empty(); +} + +class StripUnsupportedPtrauthBundlesPass + : public llvm::PassInfoMixin { +public: + llvm::PreservedAnalyses run(llvm::Module &Mod, llvm::ModuleAnalysisManager &) { + return stripUnsupportedPtrauthBundles(Mod) ? llvm::PreservedAnalyses::none() + : llvm::PreservedAnalyses::all(); + } +}; + +inline void addStripUnsupportedPtrauthBundlesPass(llvm::ModulePassManager &MPM, + bool Enabled) { + if (Enabled) + MPM.addPass(StripUnsupportedPtrauthBundlesPass()); +} + +} // namespace rustc_llvm + +#endif // INCLUDED_RUSTC_LLVM_PTRAUTHUTILS_H diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 91bb1c9733630..afeda86ff8b6a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1,4 +1,5 @@ #include "LLVMWrapper.h" +#include "PtrauthUtils.h" #include "llvm-c/Analysis.h" #include "llvm-c/Core.h" @@ -1027,6 +1028,13 @@ extern "C" void LLVMRustAddModuleFlagString( MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen))); } +extern "C" void LLVMRustAddModuleFlagMetadata( + LLVMModuleRef M, LLVMRustModuleFlagMergeBehavior MergeBehavior, + const char *Name, size_t NameLen, LLVMMetadataRef Value) { + unwrap(M)->addModuleFlag(fromRust(MergeBehavior), StringRef(Name, NameLen), + unwrap(Value)); +} + extern "C" LLVMValueRef LLVMRustGetLastInstruction(LLVMBasicBlockRef BB) { auto Point = unwrap(BB)->rbegin(); if (Point != unwrap(BB)->rend()) @@ -1599,6 +1607,10 @@ extern "C" uint64_t LLVMRustModuleInstructionStats(LLVMModuleRef M) { return unwrap(M)->getInstructionCount(); } +extern "C" void LLVMRustStripUnsupportedPtrauthBundles(LLVMModuleRef M) { + rustc_llvm::stripUnsupportedPtrauthBundles(*unwrap(M)); +} + // Transfers ownership of DiagnosticHandler unique_ptr to the caller. extern "C" DiagnosticHandler * LLVMRustContextGetDiagnosticHandler(LLVMContextRef C) { @@ -1791,14 +1803,19 @@ extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) { } extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) { - if (unwrap(V)->getType()->isPointerTy()) { - if (auto *GV = dyn_cast(unwrap(V))) { - if (GV->getValueType()->isFunctionTy()) - return false; - } - return true; + auto *Stripped = unwrap(V)->stripPointerCasts(); + if (!Stripped->getType()->isPointerTy()) + return false; + + if (isa(Stripped)) + return false; + + if (auto *GV = dyn_cast(Stripped)) { + if (GV->getValueType()->isFunctionTy()) + return false; } - return false; + + return true; } extern "C" LLVMValueRef LLVMRustStripPointerCasts(LLVMValueRef V) { diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index bc4c5e98282a5..32cf7ac944aa3 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -383,6 +383,12 @@ pub(crate) struct SmallDataThresholdNotSupportedForTarget<'a> { #[diag("`-Zbranch-protection` is only supported on aarch64")] pub(crate) struct BranchProtectionRequiresAArch64; +#[derive(Diagnostic)] +#[diag( + "`-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default" +)] +pub(crate) struct BranchProtectionPacRetRequiresNonArm64eApple; + #[derive(Diagnostic)] #[diag("`-Csplit-debuginfo={$debuginfo}` is unstable on this platform")] pub(crate) struct SplitDebugInfoUnstablePlatform { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f67feeab65732..aa1e80373d3ce 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1292,6 +1292,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) { if sess.opts.unstable_opts.branch_protection.is_some() && sess.target.arch != Arch::AArch64 { sess.dcx().emit_err(errors::BranchProtectionRequiresAArch64); } + if let Some(branch_protection) = sess.opts.unstable_opts.branch_protection + && sess.target.is_apple_arm64e() + && branch_protection.pac_ret.is_some() + { + sess.dcx().emit_err(errors::BranchProtectionPacRetRequiresNonArm64eApple); + } if let Some(dwarf_version) = sess.opts.cg.dwarf_version.or(sess.opts.unstable_opts.dwarf_version) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6910517567925..e7eac50a4f0b0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1454,6 +1454,8 @@ symbols! { overflow_checks, overlapping_marker_traits, owned_box, + paca, + pacg, packed, packed_bundled_libs, panic, diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index d1d91cbd37884..34bd6d8b4268c 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -207,6 +207,14 @@ pub(crate) fn base( (opts, unversioned_llvm_target, arch.target_arch()) } +pub(crate) fn arm64e_features(prefix: &'static str) -> StaticCow { + if prefix.is_empty() { + "+v8.3a,+paca,+pacg".into() + } else { + format!("{prefix},+v8.3a,+paca,+pacg").into() + } +} + /// Generate part of the LLVM target triple. /// /// See `rustc_codegen_ssa::back::versioned_llvm_target` for the full triple passed to LLVM and diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index d6a9e27c46553..601943dc66294 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1616,6 +1616,7 @@ supported_targets! { ("aarch64-apple-visionos", aarch64_apple_visionos), ("aarch64-apple-visionos-sim", aarch64_apple_visionos_sim), + ("arm64e-apple-visionos", arm64e_apple_visionos), ("armebv7r-none-eabi", armebv7r_none_eabi), ("armebv7r-none-eabihf", armebv7r_none_eabihf), @@ -2259,6 +2260,12 @@ impl Target { } } } + + pub fn is_apple_arm64e(&self) -> bool { + self.arch == Arch::AArch64 + && matches!(self.os, Os::MacOs | Os::IOs | Os::TvOs | Os::VisionOs) + && self.llvm_target.starts_with("arm64e") + } } pub trait HasTargetSpec { diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs index ab8f369f1bee2..301cadea8e3c4 100644 --- a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs @@ -1,4 +1,4 @@ -use crate::spec::base::apple::{Arch, TargetEnv, base}; +use crate::spec::base::apple::{Arch, TargetEnv, arm64e_features, base}; use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { @@ -16,6 +16,7 @@ pub(crate) fn target() -> Target { .into(), arch, options: TargetOptions { + features: arm64e_features(""), mcount: "\u{1}mcount".into(), cpu: "apple-m1".into(), max_atomic_width: Some(128), diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs index 396f0c347a086..d55ce2da7a14d 100644 --- a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs @@ -1,4 +1,4 @@ -use crate::spec::base::apple::{Arch, TargetEnv, base}; +use crate::spec::base::apple::{Arch, TargetEnv, arm64e_features, base}; use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { .into(), arch, options: TargetOptions { - features: "+neon,+apple-a12,+v8.3a,+paca,+pacg".into(), + features: arm64e_features("+neon,+apple-a12"), max_atomic_width: Some(128), supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::THREAD, ..opts diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs index 3cd120567a33f..984b6d6060294 100644 --- a/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs @@ -1,4 +1,4 @@ -use crate::spec::base::apple::{Arch, TargetEnv, base}; +use crate::spec::base::apple::{Arch, TargetEnv, arm64e_features, base}; use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { .into(), arch, options: TargetOptions { - features: "+neon,+apple-a12,+v8.3a,+paca,+pacg".into(), + features: arm64e_features("+neon,+apple-a12"), max_atomic_width: Some(128), ..opts }, diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_visionos.rs new file mode 100644 index 0000000000000..b1000c2c1c7eb --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_visionos.rs @@ -0,0 +1,25 @@ +use crate::spec::base::apple::{Arch, TargetEnv, base}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; + +pub(crate) fn target() -> Target { + let (opts, llvm_target, arch) = base(Os::VisionOs, Arch::Arm64e, TargetEnv::Normal); + Target { + llvm_target, + metadata: TargetMetadata { + description: Some("ARM64e Apple visionOS".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + .into(), + arch, + options: TargetOptions { + features: "+neon,+apple-a12,+v8.3a,+paca,+pacg".into(), + max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::THREAD, + ..opts + }, + } +} diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index c281aa94f64e6..805b612a53305 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -723,6 +723,70 @@ fn test_auto_ci_changed_in_pr() { }); } +#[test] +fn test_push_ci_unchanged_anywhere_uses_nightly_ref() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.set_origin_nightly_ref(&sha); + + ctx.create_branch("feature"); + ctx.modify("b"); + ctx.commit(); + + let src = ctx.check_modifications(&["c"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha }); + }); +} + +#[test] +fn test_push_ci_changed_in_branch_uses_nightly_ref() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.set_origin_nightly_ref(&sha); + + ctx.create_branch("feature"); + ctx.modify("b"); + ctx.commit(); + + let src = ctx.check_modifications(&["b"], CiEnv::GitHubActions); + assert_eq!(src, modified(sha, &["b"])); + }); +} + +#[test] +fn test_push_ci_missing_upstream_returns_missing_upstream() { + git_test(|ctx| { + ctx.create_branch("feature"); + ctx.modify("b"); + ctx.commit(); + ctx.modify("c"); + ctx.commit(); + + let src = ctx.check_modifications(&["d"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::MissingUpstream); + }); +} + +#[test] +fn test_ci_merge_without_upstream_parent_falls_back_to_nightly_ref() { + git_test(|ctx| { + let sha = ctx.create_upstream_merge(&["a"]); + ctx.set_origin_nightly_ref(&sha); + + ctx.create_branch("feature"); + ctx.modify("b"); + ctx.commit(); + ctx.create_branch("nested"); + ctx.modify("c"); + ctx.commit(); + ctx.switch_to_branch("feature"); + ctx.merge("nested", "Tester "); + + let src = ctx.check_modifications(&["d"], CiEnv::GitHubActions); + assert_eq!(src, PathFreshness::LastModifiedUpstream { upstream: sha }); + }); +} + #[test] fn test_local_uncommitted_modifications() { git_test(|ctx| { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index ca8af279b92bc..68a57b697ace4 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -35,9 +35,7 @@ pub struct Finder { /// /// Targets can be removed from this list during the usual release process bootstrap compiler bumps, /// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets. -const STAGE0_MISSING_TARGETS: &[&str] = &[ - // just a dummy comment so the list doesn't get onelined -]; +const STAGE0_MISSING_TARGETS: &[&str] = &["arm64e-apple-visionos"]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM /// from CI (with`llvm.download-ci-llvm` option). diff --git a/src/bootstrap/src/utils/tests/git.rs b/src/bootstrap/src/utils/tests/git.rs index d9dd9ab980088..ec25c72af51b1 100644 --- a/src/bootstrap/src/utils/tests/git.rs +++ b/src/bootstrap/src/utils/tests/git.rs @@ -74,6 +74,15 @@ impl GitCtx { self.run_git(&["rev-parse", "--abbrev-ref", "HEAD"]) } + pub fn set_origin_branch_ref(&self, branch: &str, commit: &str) { + let refname = format!("refs/remotes/origin/{branch}"); + self.run_git(&["update-ref", &refname, commit]); + } + + pub fn set_origin_nightly_ref(&self, commit: &str) { + self.set_origin_branch_ref(&self.nightly_branch, commit); + } + pub fn merge(&self, branch: &str, author: &str) { self.run_git(&["merge", "--no-commit", "--no-ff", branch]); self.run_git(&[ diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 87a52eee49b85..4308f1dfb1789 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -49,8 +49,9 @@ pub enum PathFreshness { /// It can be used to figure out if we should download artifacts from CI or rather /// build them locally. /// -/// The function assumes that at least a single upstream bors merge commit is in the -/// local git history. +/// If no upstream bors merge commit can be found in the available local git history, +/// the function returns `MissingUpstream` so callers can conservatively avoid using +/// CI artifacts. /// /// `target_paths` should be a non-empty slice of paths (git `pathspec`s) relative to `git_dir` /// whose modifications would invalidate the artifact. @@ -70,14 +71,11 @@ pub enum PathFreshness { /// were not modified upstream in the meantime. In that case we would be redownloading CI /// artifacts unnecessarily. /// -/// - In CI, we use a shallow clone of depth 2, i.e., we fetch only a single parent commit -/// (which will be the most recent bors merge commit) and do not have access -/// to the full git history. Luckily, we only need to distinguish between two situations: -/// 1) The current PR made modifications to `target_paths`. -/// In that case, a build is typically necessary. -/// 2) The current PR did not make modifications to `target_paths`. -/// In that case we simply take the latest upstream commit, because on CI there is no need to avoid -/// redownloading. +/// - In CI, we prefer a shallow merge-parent fast path when `HEAD` is a CI-generated merge +/// commit. However, fork push workflows can also run in shallow clones where `HEAD` is just the +/// branch tip, so blindly using `HEAD^1` there would pick a fork commit instead of the upstream +/// base. In those cases we fall back to the fetched nightly branch ref, and only then to the +/// normal upstream search logic. pub fn check_path_modifications( git_dir: &Path, config: &GitConfig<'_>, @@ -90,28 +88,12 @@ pub fn check_path_modifications( } let upstream_sha = if matches!(ci_env, CiEnv::GitHubActions) { - // Here the situation is different for PR CI and try/auto CI. - // For PR CI, we have the following history: - // - // 1-N PR commits - // upstream merge commit made by bors - // - // For try/auto CI, we have the following history: - // <**non-upstream** merge commit made by bors> - // 1-N PR commits - // upstream merge commit made by bors - // - // But on both cases, HEAD should be a merge commit. - // So if HEAD contains modifications of `target_paths`, our PR has modified - // them. If not, we can use the only available upstream commit for downloading - // artifacts. - - // Do not include HEAD, as it is never an upstream commit - // If we do not find an upstream commit in CI, something is seriously wrong. - Some( - get_closest_upstream_commit(Some(git_dir), config, ci_env)? - .expect("No upstream commit was found on CI"), - ) + // CI may be running on a synthetic merge ref or a shallow fork push ref. + // `get_closest_upstream_commit` handles the trusted merge-parent fast path and falls back + // to the fetched nightly branch ref when the merge-parent assumption is not valid. If a + // fork push clone does not have that ref either, conservatively report `MissingUpstream` + // so callers disable CI downloads and build locally instead of panicking. + get_closest_upstream_commit(Some(git_dir), config, ci_env)? } else { // Outside CI, we want to find the most recent upstream commit that // modified the set of paths, to have an upstream reference that does not change @@ -224,23 +206,45 @@ fn get_latest_upstream_commit_that_modified_files( /// Returns the most recent (ordered chronologically) commit found in the local history that /// should exist upstream. We identify upstream commits by the e-mail of the commit /// author. -/// -/// If we are in CI, we simply return our first parent. pub fn get_closest_upstream_commit( git_dir: Option<&Path>, config: &GitConfig<'_>, env: CiEnv, ) -> Result, String> { - let base = match env { - CiEnv::None => "HEAD", + match env { + CiEnv::None => get_closest_upstream_commit_from_ref(git_dir, config, "HEAD"), CiEnv::GitHubActions => { - // On CI, we should always have a non-upstream merge commit at the tip, - // and our first parent should be the most recently merged upstream commit. - // We thus simply return our first parent. - return resolve_commit_sha(git_dir, "HEAD^1").map(Some); + // CI-generated PR and auto-merge refs put a synthetic merge commit at HEAD, so the + // first parent is usually the most recent upstream merge commit. Fork push workflows + // do not have that shape, though, and in shallow clones `HEAD^1` can just be the + // previous fork commit. Only trust the fast path when it points at an actual upstream + // merge-bot commit, otherwise fall back to the fetched nightly branch. + if is_merge_commit(git_dir, "HEAD")? { + let parent = resolve_commit_sha(git_dir, "HEAD^1")?; + if is_upstream_merge_commit(git_dir, &parent, config)? { + return Ok(Some(parent)); + } + } + + let nightly_ref = format!("refs/remotes/origin/{}", config.nightly_branch); + if git_ref_exists(git_dir, &nightly_ref)? { + if let Some(upstream) = + get_closest_upstream_commit_from_ref(git_dir, config, &nightly_ref)? + { + return Ok(Some(upstream)); + } + } + + get_closest_upstream_commit_from_ref(git_dir, config, "HEAD") } - }; + } +} +fn get_closest_upstream_commit_from_ref( + git_dir: Option<&Path>, + config: &GitConfig<'_>, + base: &str, +) -> Result, String> { let mut git = Command::new("git"); if let Some(git_dir) = git_dir { @@ -272,6 +276,60 @@ pub fn get_closest_upstream_commit( if output.is_empty() { Ok(None) } else { Ok(Some(output)) } } +fn is_merge_commit(git_dir: Option<&Path>, commit_ref: &str) -> Result { + let mut git = Command::new("git"); + if let Some(git_dir) = git_dir { + git.current_dir(git_dir); + } + + let output = git + .args(["rev-parse", "--verify", "--quiet", &format!("{commit_ref}^2")]) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .status() + .map_err(|e| format!("failed to run command: {git:?}: {e}"))?; + Ok(output.success()) +} + +fn git_ref_exists(git_dir: Option<&Path>, refname: &str) -> Result { + let mut git = Command::new("git"); + if let Some(git_dir) = git_dir { + git.current_dir(git_dir); + } + + let output = git + .args(["rev-parse", "--verify", "--quiet", refname]) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .status() + .map_err(|e| format!("failed to run command: {git:?}: {e}"))?; + Ok(output.success()) +} + +fn is_upstream_merge_commit( + git_dir: Option<&Path>, + commit_ref: &str, + config: &GitConfig<'_>, +) -> Result { + let mut git = Command::new("git"); + if let Some(git_dir) = git_dir { + git.current_dir(git_dir); + } + + git.args(["show", "-s", "--format=%ae", commit_ref]); + let author_email = output_result(&mut git)?.trim().to_owned(); + let merge_bot_email = extract_author_email(config.git_merge_commit_email); + Ok(author_email == merge_bot_email || author_email == TEMPORARY_BORS_EMAIL) +} + +fn extract_author_email(author: &str) -> &str { + author + .split_once('<') + .and_then(|(_, email)| email.trim().strip_suffix('>')) + .map(str::trim) + .unwrap_or_else(|| author.trim()) +} + /// Resolve the commit SHA of `commit_ref`. fn resolve_commit_sha(git_dir: Option<&Path>, commit_ref: &str) -> Result { let mut git = Command::new("git"); diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index cc10f476780c5..915b8e7aa9a35 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -46,6 +46,7 @@ - [arm64e-apple-tvos](platform-support/arm64e-apple-tvos.md) - [\*-apple-watchos](platform-support/apple-watchos.md) - [\*-apple-visionos](platform-support/apple-visionos.md) + - [arm64e-apple-visionos](platform-support/arm64e-apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 26dd6b31b8991..99db4ad196a14 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -292,6 +292,7 @@ target | std | host | notes [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin [`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS [`arm64e-apple-tvos`](platform-support/arm64e-apple-tvos.md) | ✓ | | ARM64e Apple tvOS +[`arm64e-apple-visionos`](platform-support/arm64e-apple-visionos.md) | ✓ | | arm64e Apple visionOS [`armeb-unknown-linux-gnueabi`](platform-support/armeb-unknown-linux-gnueabi.md) | ✓ | ? | Arm BE8 the default Arm big-endian architecture since [Armv6](https://developer.arm.com/documentation/101754/0616/armlink-Reference/armlink-Command-line-Options/--be8?lang=en). [`armebv7r-none-eabi`](platform-support/armebv7r-none-eabi.md) | * | | Bare Armv7-R, Big Endian [`armebv7r-none-eabihf`](platform-support/armebv7r-none-eabi.md) | * | | Bare Armv7-R, Big Endian, hardfloat diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-visionos.md b/src/doc/rustc/src/platform-support/arm64e-apple-visionos.md new file mode 100644 index 0000000000000..a5515dc12989c --- /dev/null +++ b/src/doc/rustc/src/platform-support/arm64e-apple-visionos.md @@ -0,0 +1,46 @@ +# `arm64e-apple-visionos` + +**Tier: 3** + +Apple arm64e visionOS. + +## Target maintainers + +[@cypherair](https://github.com/cypherair) + +## Requirements + +This target is cross-compiled and supports `std`. + +This target requires the visionOS SDK provided by Xcode. + +The deployment target can be configured with `XROS_DEPLOYMENT_TARGET`. + +## Building the target + +You can build Rust with support for the target by adding it to the `target` +list in `bootstrap.toml`: +```toml +[build] +target = ["arm64e-apple-visionos"] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. +To compile for this target, you will need to build Rust with the target enabled. + +## Testing + +This target is not tested by Rust CI. + +Binaries are intended to run on visionOS devices with `arm64e` architecture. + +The visionOS simulator targets use `arm64`, not `arm64e`. + +## Cross-compilation toolchains and C code + +C code should be built with Apple's Clang from Xcode, using the visionOS SDK and +matching deployment target. + +The Clang target is suffixed with `-xros`. diff --git a/src/doc/unstable-book/src/compiler-flags/branch-protection.md b/src/doc/unstable-book/src/compiler-flags/branch-protection.md index c15567dcac2ee..265b74dc72527 100644 --- a/src/doc/unstable-book/src/compiler-flags/branch-protection.md +++ b/src/doc/unstable-book/src/compiler-flags/branch-protection.md @@ -19,6 +19,11 @@ It takes some combination of the following values, separated by a `,`. For example, `-Z branch-protection=bti,pac-ret,leaf` is valid, but `-Z branch-protection=bti,leaf,pac-ret` is not. -Rust's standard library does not ship with BTI or pointer authentication enabled by default. +On arm64e Apple targets, return-address signing is already enabled by default through +`ptrauth-returns`. Those targets still allow `bti` and `gcs`, but reject any +`-Z branch-protection` mode that includes `pac-ret`. + +Outside of arm64e Apple targets, Rust's standard library does not ship with BTI or pointer +authentication enabled by default. In Cargo projects the standard library can be recompiled with pointer authentication using the nightly [build-std](../../cargo/reference/unstable.html#build-std) feature. diff --git a/tests/assembly-llvm/arm64e-apple-ptrauth-calls.rs b/tests/assembly-llvm/arm64e-apple-ptrauth-calls.rs new file mode 100644 index 0000000000000..9839aa2b1a673 --- /dev/null +++ b/tests/assembly-llvm/arm64e-apple-ptrauth-calls.rs @@ -0,0 +1,44 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: --target arm64e-apple-darwin -Copt-level=2 +//@ needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; + +#[inline(never)] +#[no_mangle] +pub extern "C" fn target(x: i32) -> i32 { + x +} + +#[no_mangle] +pub extern "C" fn direct(x: i32) -> i32 { + target(x) +} + +#[no_mangle] +pub extern "C" fn indirect_tail(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + f(x) +} + +#[no_mangle] +pub extern "C" fn indirect_non_tail(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + match f(x) { + 0 => target(x), + y => y, + } +} + +// CHECK-LABEL: _direct: +// CHECK-NOT: braaz +// CHECK-NOT: blraaz + +// CHECK-LABEL: _indirect_tail: +// CHECK: braaz + +// CHECK-LABEL: _indirect_non_tail: +// CHECK: blraaz diff --git a/tests/assembly-llvm/targets/targets-macho.rs b/tests/assembly-llvm/targets/targets-macho.rs index 037f3c0093619..00383a6028d3e 100644 --- a/tests/assembly-llvm/targets/targets-macho.rs +++ b/tests/assembly-llvm/targets/targets-macho.rs @@ -34,6 +34,9 @@ //@ revisions: aarch64_apple_visionos //@ [aarch64_apple_visionos] compile-flags: --target aarch64-apple-visionos //@ [aarch64_apple_visionos] needs-llvm-components: aarch64 +//@ revisions: arm64e_apple_visionos +//@ [arm64e_apple_visionos] compile-flags: --target arm64e-apple-visionos +//@ [arm64e_apple_visionos] needs-llvm-components: aarch64 //@ revisions: aarch64_apple_visionos_sim //@ [aarch64_apple_visionos_sim] compile-flags: --target aarch64-apple-visionos-sim //@ [aarch64_apple_visionos_sim] needs-llvm-components: aarch64 diff --git a/tests/codegen-llvm/arm64e-apple-ptrauth-calls.rs b/tests/codegen-llvm/arm64e-apple-ptrauth-calls.rs new file mode 100644 index 0000000000000..9702fad96f1c0 --- /dev/null +++ b/tests/codegen-llvm/arm64e-apple-ptrauth-calls.rs @@ -0,0 +1,50 @@ +//@ add-minicore +//@ revisions: DARWIN IOS TVOS VISIONOS +//@ [DARWIN] compile-flags: --target arm64e-apple-darwin -Copt-level=2 +//@ [DARWIN] needs-llvm-components: aarch64 +//@ [IOS] compile-flags: --target arm64e-apple-ios -Copt-level=2 +//@ [IOS] needs-llvm-components: aarch64 +//@ [TVOS] compile-flags: --target arm64e-apple-tvos -Copt-level=2 +//@ [TVOS] needs-llvm-components: aarch64 +//@ [VISIONOS] compile-flags: --target arm64e-apple-visionos -Copt-level=2 +//@ [VISIONOS] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; + +#[inline(never)] +#[no_mangle] +pub extern "C" fn target(x: i32) -> i32 { + x +} + +#[no_mangle] +pub extern "C" fn direct(x: i32) -> i32 { + target(x) +} + +#[no_mangle] +pub extern "C" fn indirect_tail(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + f(x) +} + +#[no_mangle] +pub extern "C" fn indirect_non_tail(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + match f(x) { + 0 => target(x), + y => y, + } +} + +// CHECK-LABEL: define{{.*}} @direct( +// CHECK-NOT: [ "ptrauth"(i32 0, i64 0) ] +// CHECK: ret i32 + +// CHECK-LABEL: define{{.*}} @indirect_tail( +// CHECK: {{(tail )?}}call{{.*}} {{%.*}}(i32 {{.*}}){{.*}} [ "ptrauth"(i32 0, i64 0) ]{{$}} + +// CHECK-LABEL: define{{.*}} @indirect_non_tail( +// CHECK: {{(tail )?}}call{{.*}} {{%.*}}(i32 {{.*}}){{.*}} [ "ptrauth"(i32 0, i64 0) ]{{$}} diff --git a/tests/codegen-llvm/arm64e-apple-ptrauth-entry.rs b/tests/codegen-llvm/arm64e-apple-ptrauth-entry.rs new file mode 100644 index 0000000000000..ebb6875dfb9e8 --- /dev/null +++ b/tests/codegen-llvm/arm64e-apple-ptrauth-entry.rs @@ -0,0 +1,29 @@ +//@ add-minicore +//@ revisions: DARWIN IOS TVOS +//@ [DARWIN] compile-flags: --target arm64e-apple-darwin -Copt-level=0 +//@ [DARWIN] needs-llvm-components: aarch64 +//@ [IOS] compile-flags: --target arm64e-apple-ios -Copt-level=0 +//@ [IOS] needs-llvm-components: aarch64 +//@ [TVOS] compile-flags: --target arm64e-apple-tvos -Copt-level=0 +//@ [TVOS] needs-llvm-components: aarch64 +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[lang = "start"] +fn lang_start( + _main: fn() -> T, + _argc: isize, + _argv: *const *const u8, + _sigpipe: u8, +) -> isize { + 0 +} + +fn main() {} + +// CHECK: define i32 @main({{.*}}) unnamed_addr [[MAIN_ATTR:#[0-9]+]] +// CHECK: attributes [[MAIN_ATTR]] = {{.*}} "ptrauth-auth-traps" "ptrauth-calls" +// CHECK-SAME: "ptrauth-indirect-gotos" "ptrauth-returns" diff --git a/tests/codegen-llvm/arm64e-apple-ptrauth-fnptr-data.rs b/tests/codegen-llvm/arm64e-apple-ptrauth-fnptr-data.rs new file mode 100644 index 0000000000000..41a87c2c860c8 --- /dev/null +++ b/tests/codegen-llvm/arm64e-apple-ptrauth-fnptr-data.rs @@ -0,0 +1,38 @@ +//@ add-minicore +//@ revisions: DARWIN IOS TVOS VISIONOS +//@ [DARWIN] compile-flags: --target arm64e-apple-darwin -Copt-level=0 -Zverify-llvm-ir +//@ [DARWIN] needs-llvm-components: aarch64 +//@ [IOS] compile-flags: --target arm64e-apple-ios -Copt-level=0 -Zverify-llvm-ir +//@ [IOS] needs-llvm-components: aarch64 +//@ [TVOS] compile-flags: --target arm64e-apple-tvos -Copt-level=0 -Zverify-llvm-ir +//@ [TVOS] needs-llvm-components: aarch64 +//@ [VISIONOS] compile-flags: --target arm64e-apple-visionos -Copt-level=0 -Zverify-llvm-ir +//@ [VISIONOS] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; + +#[unsafe(no_mangle)] +pub extern "C" fn invoke_main(main_fn: extern "C" fn(*const u8) -> i32, state: *const u8) -> i32 { + main_fn(state) +} + +#[unsafe(no_mangle)] +pub extern "C" fn sample_main(_: *const u8) -> i32 { + 7 +} + +#[unsafe(no_mangle)] +pub extern "C" fn call_sample() -> i32 { + let f = sample_main; + invoke_main(f, 0 as *const u8) +} + +// CHECK-LABEL: define{{.*}} i32 @invoke_main( +// CHECK: call i32 %{{.*}}(ptr %{{.*}}){{.*}} [ "ptrauth"(i32 0, i64 0) ] + +// CHECK-LABEL: define{{.*}} i32 @call_sample( +// CHECK: call i32 @invoke_main(ptr ptrauth (ptr @sample_main, i32 0), ptr null) diff --git a/tests/codegen-llvm/arm64e-apple-ptrauth-inline-asm.rs b/tests/codegen-llvm/arm64e-apple-ptrauth-inline-asm.rs new file mode 100644 index 0000000000000..99e8e47510759 --- /dev/null +++ b/tests/codegen-llvm/arm64e-apple-ptrauth-inline-asm.rs @@ -0,0 +1,24 @@ +//@ add-minicore +//@ revisions: DARWIN IOS TVOS +//@ [DARWIN] compile-flags: --target arm64e-apple-darwin +//@ [DARWIN] needs-llvm-components: aarch64 +//@ [IOS] compile-flags: --target arm64e-apple-ios +//@ [IOS] needs-llvm-components: aarch64 +//@ [TVOS] compile-flags: --target arm64e-apple-tvos +//@ [TVOS] needs-llvm-components: aarch64 +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub unsafe fn inline_asm() { + unsafe { asm!("nop") }; +} + +// CHECK-LABEL: define{{.*}} @inline_asm( +// CHECK: call void asm sideeffect alignstack "nop" +// CHECK-NOT: [ "ptrauth"(i32 0, i64 0) ] +// CHECK: ret void diff --git a/tests/codegen-llvm/arm64e-apple-ptrauth-invoke.rs b/tests/codegen-llvm/arm64e-apple-ptrauth-invoke.rs new file mode 100644 index 0000000000000..cc4dab4d410fc --- /dev/null +++ b/tests/codegen-llvm/arm64e-apple-ptrauth-invoke.rs @@ -0,0 +1,36 @@ +// ignore-tidy-linelength +//@ add-minicore +//@ revisions: DARWIN IOS TVOS VISIONOS +//@ [DARWIN] compile-flags: --target arm64e-apple-darwin -Copt-level=0 -Cpanic=unwind -Zverify-llvm-ir +//@ [DARWIN] needs-llvm-components: aarch64 +//@ [IOS] compile-flags: --target arm64e-apple-ios -Copt-level=0 -Cpanic=unwind -Zverify-llvm-ir +//@ [IOS] needs-llvm-components: aarch64 +//@ [TVOS] compile-flags: --target arm64e-apple-tvos -Copt-level=0 -Cpanic=unwind -Zverify-llvm-ir +//@ [TVOS] needs-llvm-components: aarch64 +//@ [VISIONOS] compile-flags: --target arm64e-apple-visionos -Copt-level=0 -Cpanic=unwind -Zverify-llvm-ir +//@ [VISIONOS] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(intrinsics, no_core, lang_items)] +#![no_core] + +extern crate minicore; + +#[rustc_intrinsic] +pub unsafe fn catch_unwind( + try_fn: fn(*mut u8), + data: *mut u8, + catch_fn: fn(*mut u8, *mut u8), +) -> i32; + +fn try_fn(_: *mut u8) {} +fn catch_fn(_: *mut u8, _: *mut u8) {} + +#[unsafe(no_mangle)] +pub unsafe fn invoke_catch() -> i32 { + unsafe { catch_unwind(try_fn, 0 as *mut u8, catch_fn) } +} + +// CHECK: define internal i32 @__rust_try({{.*}}) unnamed_addr [[TRY_ATTR:#[0-9]+]] personality +// CHECK: invoke void %{{.*}}(ptr %{{.*}}){{.*}} [ "ptrauth"(i32 0, i64 0) ] +// CHECK: attributes [[TRY_ATTR]] = {{.*}} "ptrauth-auth-traps" "ptrauth-calls" "ptrauth-indirect-gotos" "ptrauth-returns" diff --git a/tests/codegen-llvm/arm64e-apple-ptrauth.rs b/tests/codegen-llvm/arm64e-apple-ptrauth.rs new file mode 100644 index 0000000000000..60e25b4cd691e --- /dev/null +++ b/tests/codegen-llvm/arm64e-apple-ptrauth.rs @@ -0,0 +1,34 @@ +//@ add-minicore +//@ revisions: DARWIN DARWIN_BTI DARWIN_GCS IOS TVOS VISIONOS +//@ [DARWIN] compile-flags: --target arm64e-apple-darwin +//@ [DARWIN] needs-llvm-components: aarch64 +//@ [DARWIN_BTI] compile-flags: --target arm64e-apple-darwin -Zbranch-protection=bti +//@ [DARWIN_BTI] needs-llvm-components: aarch64 +//@ [DARWIN_GCS] compile-flags: --target arm64e-apple-darwin -Zbranch-protection=gcs +//@ [DARWIN_GCS] needs-llvm-components: aarch64 +//@ [IOS] compile-flags: --target arm64e-apple-ios +//@ [IOS] needs-llvm-components: aarch64 +//@ [TVOS] compile-flags: --target arm64e-apple-tvos +//@ [TVOS] needs-llvm-components: aarch64 +//@ [VISIONOS] compile-flags: --target arm64e-apple-visionos +//@ [VISIONOS] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK: @test(){{.*}} [[ATTR:#[0-9]+]] { +#[no_mangle] +pub fn test() {} + +// CHECK: attributes [[ATTR]] = {{.*}} "ptrauth-auth-traps" "ptrauth-calls" "ptrauth-indirect-gotos" "ptrauth-returns" +// CHECK-SAME: "target-features"="{{.*}}+pauth{{.*}}" +// DARWIN_BTI: !{{[0-9]+}} = !{i32 8, !"branch-target-enforcement", i32 1} +// DARWIN_GCS: !{{[0-9]+}} = !{i32 8, !"guarded-control-stack", i32 1} + +// CHECK: !{{[0-9]+}} = !{i32 6, !"ptrauth.abi-version", ![[OUTER:[0-9]+]]} +// CHECK: ![[OUTER]] = !{![[PAYLOAD:[0-9]+]]} +// CHECK: ![[PAYLOAD]] = !{i32 0, i1 false} diff --git a/tests/run-make/arm64e-ptrauth-strip-emit/ptrauth.rs b/tests/run-make/arm64e-ptrauth-strip-emit/ptrauth.rs new file mode 100644 index 0000000000000..f06a241238e4b --- /dev/null +++ b/tests/run-make/arm64e-ptrauth-strip-emit/ptrauth.rs @@ -0,0 +1,31 @@ +#![crate_type = "lib"] +#![feature(lang_items, no_core)] +#![no_core] + +#[lang = "pointee_sized"] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} + +#[lang = "sized"] +pub trait Sized: MetaSized {} + +#[lang = "copy"] +pub trait Copy: Sized {} + +#[inline(never)] +#[no_mangle] +pub extern "C" fn target(x: i32) -> i32 { + x +} + +#[inline(always)] +fn call_indirect(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + f(x) +} + +#[no_mangle] +pub extern "C" fn devirtualized(x: i32) -> i32 { + call_indirect(target, x) +} diff --git a/tests/run-make/arm64e-ptrauth-strip-emit/rmake.rs b/tests/run-make/arm64e-ptrauth-strip-emit/rmake.rs new file mode 100644 index 0000000000000..c9e89c5565732 --- /dev/null +++ b/tests/run-make/arm64e-ptrauth-strip-emit/rmake.rs @@ -0,0 +1,54 @@ +//@ needs-llvm-components: aarch64 + +use std::path::{Path, PathBuf}; + +use run_make_support::{llvm_ar, llvm_dis, llvm_objcopy, rfs, rustc}; + +fn assert_no_direct_ptrauth(path: &str) { + let ir = rfs::read_to_string(path); + for (line_idx, line) in ir.lines().enumerate() { + if line.contains("@target(") && line.contains("\"ptrauth\"") { + panic!( + "{path}:{} still contains a direct call with a ptrauth operand bundle:\n{line}", + line_idx + 1 + ); + } + } +} + +fn main() { + rustc() + .input("ptrauth.rs") + .target("arm64e-apple-darwin") + .opt_level("2") + .arg("--emit=llvm-ir=module.ll,llvm-bc=module.bc") + .run(); + assert_no_direct_ptrauth("module.ll"); + + llvm_dis().input("module.bc").arg("-o").arg("module-bc.ll").run(); + assert_no_direct_ptrauth("module-bc.ll"); + + rustc() + .input("ptrauth.rs") + .target("arm64e-apple-darwin") + .opt_level("2") + .arg("-Cembed-bitcode=yes") + .crate_type("rlib") + .arg("-o") + .arg("libptrauth.rlib") + .run(); + + if Path::new("extract").exists() { + rfs::recursive_remove("extract"); + } + rfs::create_dir("extract"); + llvm_ar().extract().arg("../libptrauth.rlib").current_dir("extract").run(); + let object = rfs::read_dir("extract") + .map(|entry| entry.unwrap().path()) + .find(|path| path.extension().is_some_and(|ext| ext == "o")) + .expect("embedded-bitcode rlib should contain an object file"); + let embedded_bc = PathBuf::from("extract").join("embedded.bc"); + llvm_objcopy().dump_section("__LLVM,__bitcode", &embedded_bc).arg(&object).run(); + llvm_dis().input(&embedded_bc).arg("-o").arg("embedded.ll").run(); + assert_no_direct_ptrauth("embedded.ll"); +} diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PACRET.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PACRET.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PACRET.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PAUTHLR_LEAF.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PAUTHLR_LEAF.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PAUTHLR_LEAF.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PACRET.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PACRET.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PACRET.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PAUTHLR_LEAF.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PAUTHLR_LEAF.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PAUTHLR_LEAF.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PACRET.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PACRET.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PACRET.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PAUTHLR_LEAF.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PAUTHLR_LEAF.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PAUTHLR_LEAF.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PACRET.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PACRET.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PACRET.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PAUTHLR_LEAF.stderr b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PAUTHLR_LEAF.stderr new file mode 100644 index 0000000000000..50d4347075124 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PAUTHLR_LEAF.stderr @@ -0,0 +1,3 @@ +error: `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.rs b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.rs new file mode 100644 index 0000000000000..f03cf5ff8deaa --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.rs @@ -0,0 +1,44 @@ +// ignore-tidy-linelength +//@ ignore-backends: gcc +// gcc does not model these LLVM AArch64 Apple arm64e ptrauth diagnostics. +//@ normalize-stderr: "\nerror: aborting due to 1 previous error\n\n" -> "\nerror: aborting due to 1 previous error\n" +//@ revisions: DARWIN_PACRET DARWIN_PAUTHLR_LEAF IOS_PACRET IOS_PAUTHLR_LEAF TVOS_PACRET TVOS_PAUTHLR_LEAF VISIONOS_PACRET VISIONOS_PAUTHLR_LEAF +//@ [DARWIN_PACRET] compile-flags: --target=arm64e-apple-darwin -Zbranch-protection=pac-ret +//@ [DARWIN_PACRET] check-fail +//@ [DARWIN_PACRET] needs-llvm-components: aarch64 +//@ [DARWIN_PAUTHLR_LEAF] compile-flags: --target=arm64e-apple-darwin -Zbranch-protection=pac-ret,pc,leaf +//@ [DARWIN_PAUTHLR_LEAF] check-fail +//@ [DARWIN_PAUTHLR_LEAF] needs-llvm-components: aarch64 +//@ [IOS_PACRET] compile-flags: --target=arm64e-apple-ios -Zbranch-protection=pac-ret +//@ [IOS_PACRET] check-fail +//@ [IOS_PACRET] needs-llvm-components: aarch64 +//@ [IOS_PAUTHLR_LEAF] compile-flags: --target=arm64e-apple-ios -Zbranch-protection=pac-ret,pc,leaf +//@ [IOS_PAUTHLR_LEAF] check-fail +//@ [IOS_PAUTHLR_LEAF] needs-llvm-components: aarch64 +//@ [TVOS_PACRET] compile-flags: --target=arm64e-apple-tvos -Zbranch-protection=pac-ret +//@ [TVOS_PACRET] check-fail +//@ [TVOS_PACRET] needs-llvm-components: aarch64 +//@ [TVOS_PAUTHLR_LEAF] compile-flags: --target=arm64e-apple-tvos -Zbranch-protection=pac-ret,pc,leaf +//@ [TVOS_PAUTHLR_LEAF] check-fail +//@ [TVOS_PAUTHLR_LEAF] needs-llvm-components: aarch64 +//@ [VISIONOS_PACRET] compile-flags: --target=arm64e-apple-visionos -Zbranch-protection=pac-ret +//@ [VISIONOS_PACRET] check-fail +//@ [VISIONOS_PACRET] needs-llvm-components: aarch64 +//@ [VISIONOS_PAUTHLR_LEAF] compile-flags: --target=arm64e-apple-visionos -Zbranch-protection=pac-ret,pc,leaf +//@ [VISIONOS_PAUTHLR_LEAF] check-fail +//@ [VISIONOS_PAUTHLR_LEAF] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "pointee_sized"] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} + +#[lang = "sized"] +pub trait Sized: MetaSized {} + +//~? ERROR `-Zbranch-protection` with `pac-ret` is incompatible with arm64e Apple targets because they enable `ptrauth-returns` by default diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_BOTH.stderr b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_BOTH.stderr new file mode 100644 index 0000000000000..40c7e4aa648de --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_BOTH.stderr @@ -0,0 +1,3 @@ +error: `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACA.stderr b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACA.stderr new file mode 100644 index 0000000000000..40c7e4aa648de --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACA.stderr @@ -0,0 +1,3 @@ +error: `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACG.stderr b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACG.stderr new file mode 100644 index 0000000000000..40c7e4aa648de --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACG.stderr @@ -0,0 +1,3 @@ +error: `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PAUTH.stderr b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PAUTH.stderr new file mode 100644 index 0000000000000..40c7e4aa648de --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PAUTH.stderr @@ -0,0 +1,3 @@ +error: `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.IOS_BOTH.stderr b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.IOS_BOTH.stderr new file mode 100644 index 0000000000000..40c7e4aa648de --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.IOS_BOTH.stderr @@ -0,0 +1,3 @@ +error: `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.TVOS_BOTH.stderr b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.TVOS_BOTH.stderr new file mode 100644 index 0000000000000..40c7e4aa648de --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.TVOS_BOTH.stderr @@ -0,0 +1,3 @@ +error: `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.VISIONOS_BOTH.stderr b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.VISIONOS_BOTH.stderr new file mode 100644 index 0000000000000..40c7e4aa648de --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.VISIONOS_BOTH.stderr @@ -0,0 +1,3 @@ +error: `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default + +error: aborting due to 1 previous error diff --git a/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.rs b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.rs new file mode 100644 index 0000000000000..942bed9c94c64 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.rs @@ -0,0 +1,40 @@ +//@ ignore-backends: gcc +// gcc does not model these LLVM AArch64 Apple arm64e ptrauth diagnostics. +//@ normalize-stderr: "\nerror: aborting due to 1 previous error\n\n" -> "\nerror: aborting due to 1 previous error\n" +//@ revisions: DARWIN_PACA DARWIN_PACG DARWIN_PAUTH DARWIN_BOTH IOS_BOTH TVOS_BOTH VISIONOS_BOTH +//@ [DARWIN_PACA] compile-flags: --target=arm64e-apple-darwin -Ctarget-feature=-paca +//@ [DARWIN_PACA] check-fail +//@ [DARWIN_PACA] needs-llvm-components: aarch64 +//@ [DARWIN_PACG] compile-flags: --target=arm64e-apple-darwin -Ctarget-feature=-pacg +//@ [DARWIN_PACG] check-fail +//@ [DARWIN_PACG] needs-llvm-components: aarch64 +//@ [DARWIN_PAUTH] compile-flags: --target=arm64e-apple-darwin -Ctarget-feature=-pauth +//@ [DARWIN_PAUTH] check-fail +//@ [DARWIN_PAUTH] needs-llvm-components: aarch64 +//@ [DARWIN_BOTH] compile-flags: --target=arm64e-apple-darwin -Ctarget-feature=-paca,-pacg +//@ [DARWIN_BOTH] check-fail +//@ [DARWIN_BOTH] needs-llvm-components: aarch64 +//@ [IOS_BOTH] compile-flags: --target=arm64e-apple-ios -Ctarget-feature=-paca,-pacg +//@ [IOS_BOTH] check-fail +//@ [IOS_BOTH] needs-llvm-components: aarch64 +//@ [TVOS_BOTH] compile-flags: --target=arm64e-apple-tvos -Ctarget-feature=-paca,-pacg +//@ [TVOS_BOTH] check-fail +//@ [TVOS_BOTH] needs-llvm-components: aarch64 +//@ [VISIONOS_BOTH] compile-flags: --target=arm64e-apple-visionos -Ctarget-feature=-paca,-pacg +//@ [VISIONOS_BOTH] check-fail +//@ [VISIONOS_BOTH] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "pointee_sized"] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} + +#[lang = "sized"] +pub trait Sized: MetaSized {} + +//~? ERROR `-Ctarget-feature` cannot disable `paca`, `pacg`, or LLVM `pauth` on arm64e Apple targets because they enable ptrauth by default