diff --git a/.github/workflows/fork-arm64e.yml b/.github/workflows/fork-arm64e.yml new file mode 100644 index 0000000000000..3d7ef68fa390c --- /dev/null +++ b/.github/workflows/fork-arm64e.yml @@ -0,0 +1,416 @@ +name: Fork arm64e validation + +on: + workflow_dispatch: + push: + branches: + - codex/arm64e-darwin-ptrauth-spike + pull_request: + branches: + - main + +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/codegen-llvm/arm64e-apple-ptrauth-calls.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 intentionally non-blocking while runner and TLS isolation is being investigated.") + PY + + { + echo "Smoke diagnostic JSON files:" + ls -1 /tmp/arm64e-diagnostics/*.json + } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 1f59d250e08a0..a4b4ef9dfd287 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -578,6 +578,13 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } + if has_default_arm64e_ptrauth(sess) { + to_add.push(llvm::CreateAttrString(cx.llcx, "ptrauth-auth-traps")); + to_add.push(llvm::CreateAttrString(cx.llcx, "ptrauth-calls")); + to_add.push(llvm::CreateAttrString(cx.llcx, "ptrauth-indirect-gotos")); + to_add.push(llvm::CreateAttrString(cx.llcx, "ptrauth-returns")); + } + let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones @@ -613,3 +620,9 @@ 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) +} diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 515b571a9f4b3..24153c043c4f7 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -70,6 +70,12 @@ fn write_output_file<'ll>( } else { std::ptr::null() }; + + // 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); @@ -742,8 +748,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) { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 056a0763087a2..546b097cc7e6e 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); @@ -1388,10 +1408,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); @@ -1835,7 +1859,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); } @@ -1850,6 +1874,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 dadf8e9e7d5fa..8463f1e9cd636 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..5fec8fae9446f 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. // diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 65c70c754918d..f9d6a126eff73 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 525d1dbe9d0d3..019194b4d1505 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 @@ -2185,6 +2191,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, @@ -2494,6 +2508,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 f6544c0351457..89711eea97570 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -216,7 +216,9 @@ pub(crate) fn create_object_file(sess: &Session) -> Option 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/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 50c439593c306..c46903f6f8963 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 f1112510af0f0..71e2292181b21 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1211,6 +1211,12 @@ pub(crate) struct ForbiddenCTargetFeature<'a> { pub reason: &'a str, } +#[derive(Diagnostic)] +#[diag( + "`-Ctarget-feature` cannot disable `paca` or `pacg` 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 f9e4a6a352bac..10cdfbbe3a546 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 24f731c01996d..a1b5c55c1e4d5 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -254,6 +254,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( @@ -264,6 +265,14 @@ pub fn cfg_target_feature<'a, const N: usize>( sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature }); }, |base_feature, new_features, enable| { + if !enable && sess.target.is_apple_arm64e() && matches!(base_feature, "paca" | "pacg") { + 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))); 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/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index c310e580af559..e81a0d169b547 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1011,6 +1011,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()) @@ -1583,6 +1590,40 @@ extern "C" uint64_t LLVMRustModuleInstructionStats(LLVMModuleRef M) { return unwrap(M)->getInstructionCount(); } +extern "C" void LLVMRustStripUnsupportedPtrauthBundles(LLVMModuleRef M) { + Module *Mod = unwrap(M); + LLVMContext &Ctx = Mod->getContext(); + const uint32_t PtrauthID = Ctx.getOperandBundleTagID("ptrauth"); + SmallVector ToRewrite; + + for (Function &F : *Mod) { + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + auto *CB = dyn_cast(&I); + if (!CB || !CB->countOperandBundlesOfType(PtrauthID)) + continue; + + bool ShouldStrip = isa(CB); + if (!ShouldStrip) { + Value *CalledOperand = CB->getCalledOperand()->stripPointerCasts(); + if (auto *GV = dyn_cast(CalledOperand)) + ShouldStrip = GV->getValueType()->isFunctionTy(); + } + + if (ShouldStrip) + ToRewrite.push_back(CB); + } + } + } + + for (CallBase *CB : ToRewrite) { + CallBase *Replacement = + CallBase::removeOperandBundle(CB, PtrauthID, CB->getIterator()); + CB->replaceAllUsesWith(Replacement); + CB->eraseFromParent(); + } +} + // Transfers ownership of DiagnosticHandler unique_ptr to the caller. extern "C" DiagnosticHandler * LLVMRustContextGetDiagnosticHandler(LLVMContextRef C) { @@ -1775,14 +1816,16 @@ 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 (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 54f88aa22bdc4..fab5dc50bf929 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -218,6 +218,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 a9e7f1503b9ca..67d7221e47db3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1282,6 +1282,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 e7574eaed5b27..b1129d86a1e35 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1449,6 +1449,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 768e43146a0c1..986a17e2a8ede 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1615,6 +1615,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), @@ -2234,6 +2235,12 @@ impl Target { | X86_64 | Xtensa => true, } } + + 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..170138d575d57 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_visionos.rs @@ -0,0 +1,25 @@ +use crate::spec::base::apple::{Arch, TargetEnv, arm64e_features, 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: arm64e_features("+neon,+apple-a12"), + 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..50903f74733c2 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -723,6 +723,56 @@ 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_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..64e86a651ab00 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -70,14 +70,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,24 +87,9 @@ 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. + // 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. Some( get_closest_upstream_commit(Some(git_dir), config, ci_env)? .expect("No upstream commit was found on CI"), @@ -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..dc8a982e3b7a7 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/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index cd183e7d635da..92761322f719d 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -6,6 +6,7 @@ Apple visionOS / xrOS targets. - `aarch64-apple-visionos`: Apple visionOS on arm64. - `aarch64-apple-visionos-sim`: Apple visionOS Simulator on arm64. +- `arm64e-apple-visionos`: Apple visionOS on arm64e. ## Target maintainers @@ -37,6 +38,14 @@ $ rustup target add aarch64-apple-visionos $ rustup target add aarch64-apple-visionos-sim ``` +For the arm64e target, Rust does not currently ship pre-built artifacts. Build +Rust with the target enabled in `bootstrap.toml`: + +```toml +[build] +target = ["arm64e-apple-visionos"] +``` + ## Building Rust programs See [the instructions for iOS](./apple-ios.md#building-rust-programs). 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..b0c27aa792ac7 --- /dev/null +++ b/src/doc/rustc/src/platform-support/arm64e-apple-visionos.md @@ -0,0 +1,41 @@ +# `arm64e-apple-visionos` + +**Tier: 3** + +ARM64e visionOS (1.0+) + +## Target maintainers + +[@arttet](https://github.com/arttet) + +## Requirements + +See the docs on [`*-apple-visionos`](apple-visionos.md) for general visionOS +requirements. + +## 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 +(see [Building the target](#building-the-target) above). + +## Testing + +The target does support running binaries on visionOS platforms with `arm64e` +architecture. + +## Cross-compilation toolchains and C code + +The target does support `C` code. +To build compatible `C` code, you have to use Xcode with the same compiler and +flags. 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-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-invoke.rs b/tests/codegen-llvm/arm64e-apple-ptrauth-invoke.rs new file mode 100644 index 0000000000000..5e379f3739c13 --- /dev/null +++ b/tests/codegen-llvm/arm64e-apple-ptrauth-invoke.rs @@ -0,0 +1,35 @@ +// 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 {{.*}}@__rust_try( +// CHECK: invoke void %{{.*}}(ptr %{{.*}}){{.*}} [ "ptrauth"(i32 0, i64 0) ] 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/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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PACRET.stderr @@ -0,0 +1,4 @@ +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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.DARWIN_PAUTHLR_LEAF.stderr @@ -0,0 +1,4 @@ +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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PACRET.stderr @@ -0,0 +1,4 @@ +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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.IOS_PAUTHLR_LEAF.stderr @@ -0,0 +1,4 @@ +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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PACRET.stderr @@ -0,0 +1,4 @@ +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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.TVOS_PAUTHLR_LEAF.stderr @@ -0,0 +1,4 @@ +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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PACRET.stderr @@ -0,0 +1,4 @@ +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..1f1910f6ddb62 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.VISIONOS_PAUTHLR_LEAF.stderr @@ -0,0 +1,4 @@ +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..cabc06cc9d5d1 --- /dev/null +++ b/tests/ui/compile-flags/invalid/branch-protection-arm64e-apple-pac-ret.rs @@ -0,0 +1,42 @@ +// ignore-tidy-linelength +//@ ignore-backends: gcc +//@ 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..b4ab796c1eb59 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_BOTH.stderr @@ -0,0 +1,4 @@ +error: `-Ctarget-feature` cannot disable `paca` or `pacg` 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..b4ab796c1eb59 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACA.stderr @@ -0,0 +1,4 @@ +error: `-Ctarget-feature` cannot disable `paca` or `pacg` 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..b4ab796c1eb59 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.DARWIN_PACG.stderr @@ -0,0 +1,4 @@ +error: `-Ctarget-feature` cannot disable `paca` or `pacg` 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..b4ab796c1eb59 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.IOS_BOTH.stderr @@ -0,0 +1,4 @@ +error: `-Ctarget-feature` cannot disable `paca` or `pacg` 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..b4ab796c1eb59 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.TVOS_BOTH.stderr @@ -0,0 +1,4 @@ +error: `-Ctarget-feature` cannot disable `paca` or `pacg` 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..b4ab796c1eb59 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.VISIONOS_BOTH.stderr @@ -0,0 +1,4 @@ +error: `-Ctarget-feature` cannot disable `paca` or `pacg` 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..5eeec7abc73a3 --- /dev/null +++ b/tests/ui/compile-flags/invalid/target-feature-arm64e-apple-ptrauth-disable.rs @@ -0,0 +1,35 @@ +//@ ignore-backends: gcc +//@ revisions: DARWIN_PACA DARWIN_PACG 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_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` or `pacg` on arm64e Apple targets because they enable ptrauth by default