From a030ffbe354be8072fe2749d89c66365c192b0fe Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 1 Dec 2024 19:07:13 -0800 Subject: [PATCH 1/2] Add `core::arch::breakpoint` and test Approved in [ACP 491](https://github.com/rust-lang/libs-team/issues/491). Remove the `unsafe` on `core::intrinsics::breakpoint()`, since it's a safe intrinsic to call and has no prerequisites. (Thanks to @zachs18 for figuring out the `bootstrap`/`not(bootstrap)` logic.) --- .../src/error_codes/E0622.md | 9 ++++--- .../rustc_hir_analysis/src/check/intrinsic.rs | 1 + library/core/src/arch.rs | 27 +++++++++++++++++++ library/core/src/intrinsics/mod.rs | 12 +++++++++ tests/assembly/breakpoint.rs | 14 ++++++++++ tests/ui/error-codes/E0622.rs | 4 +-- tests/ui/error-codes/E0622.stderr | 4 +-- 7 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 tests/assembly/breakpoint.rs diff --git a/compiler/rustc_error_codes/src/error_codes/E0622.md b/compiler/rustc_error_codes/src/error_codes/E0622.md index 5d71ee9949d86..4cb605b636d2e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0622.md +++ b/compiler/rustc_error_codes/src/error_codes/E0622.md @@ -7,10 +7,11 @@ Erroneous code example: #![allow(internal_features)] extern "rust-intrinsic" { - pub static breakpoint: fn(); // error: intrinsic must be a function + pub static atomic_singlethreadfence_seqcst: fn(); + // error: intrinsic must be a function } -fn main() { unsafe { breakpoint(); } } +fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } ``` An intrinsic is a function available for use in a given programming language @@ -22,8 +23,8 @@ error, just declare a function. Example: #![allow(internal_features)] extern "rust-intrinsic" { - pub fn breakpoint(); // ok! + pub fn atomic_singlethreadfence_seqcst(); // ok! } -fn main() { unsafe { breakpoint(); } } +fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } ``` diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3e33120901f69..31a9b34ee0c77 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -87,6 +87,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid + | sym::breakpoint | sym::size_of | sym::min_align_of | sym::needs_drop diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 57f456c98b3c6..95d88c7f67991 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -42,3 +42,30 @@ pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } + +/// Compiles to a target-specific software breakpoint instruction or equivalent. +/// +/// This will typically abort the program. It may result in a core dump, and/or the system logging +/// debug information. Additional target-specific capabilities may be possible depending on +/// debuggers or other tooling; in particular, a debugger may be able to resume execution. +/// +/// If possible, this will produce an instruction sequence that allows a debugger to resume *after* +/// the breakpoint, rather than resuming *at* the breakpoint; however, the exact behavior is +/// target-specific and debugger-specific, and not guaranteed. +/// +/// If the target platform does not have any kind of debug breakpoint instruction, this may compile +/// to a trapping instruction (e.g. an undefined instruction) instead, or to some other form of +/// target-specific abort that may or may not support convenient resumption. +/// +/// The precise behavior and the precise instruction generated are not guaranteed, except that in +/// normal execution with no debug tooling involved this will not continue executing. +/// +/// - On x86 targets, this produces an `int3` instruction. +/// - On aarch64 targets, this produces a `brk #0xf000` instruction. +// When stabilizing this, update the comment on `core::intrinsics::breakpoint`. +#[unstable(feature = "breakpoint", issue = "133724")] +#[inline(always)] +#[cfg(not(bootstrap))] +pub fn breakpoint() { + core::intrinsics::breakpoint(); +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 46873fdc0479f..4c8fd922f1958 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1381,6 +1381,18 @@ pub unsafe fn prefetch_write_instruction(_data: *const T, _locality: i32) { #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] #[rustc_nounwind] +#[cfg(not(bootstrap))] +pub fn breakpoint() { + unreachable!() +} + +/// Executes a breakpoint trap, for inspection by a debugger. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +#[cfg(bootstrap)] pub unsafe fn breakpoint() { unreachable!() } diff --git a/tests/assembly/breakpoint.rs b/tests/assembly/breakpoint.rs new file mode 100644 index 0000000000000..e0cc2d1eebb74 --- /dev/null +++ b/tests/assembly/breakpoint.rs @@ -0,0 +1,14 @@ +//@ revisions: aarch64 x86_64 +//@ assembly-output: emit-asm +//@[aarch64] only-aarch64 +//@[x86_64] only-x86_64 + +#![feature(breakpoint)] +#![crate_type = "lib"] + +// CHECK-LABEL: use_bp +// aarch64: brk #0xf000 +// x86_64: int3 +pub fn use_bp() { + core::arch::breakpoint(); +} diff --git a/tests/ui/error-codes/E0622.rs b/tests/ui/error-codes/E0622.rs index ae7378a707e5c..08c6d17129604 100644 --- a/tests/ui/error-codes/E0622.rs +++ b/tests/ui/error-codes/E0622.rs @@ -1,6 +1,6 @@ #![feature(intrinsics)] extern "rust-intrinsic" { - pub static breakpoint : unsafe extern "rust-intrinsic" fn(); + pub static atomic_singlethreadfence_seqcst : unsafe extern "rust-intrinsic" fn(); //~^ ERROR intrinsic must be a function [E0622] } -fn main() { unsafe { breakpoint(); } } +fn main() { unsafe { atomic_singlethreadfence_seqcst(); } } diff --git a/tests/ui/error-codes/E0622.stderr b/tests/ui/error-codes/E0622.stderr index c59776b211fd6..739ec984fc606 100644 --- a/tests/ui/error-codes/E0622.stderr +++ b/tests/ui/error-codes/E0622.stderr @@ -1,8 +1,8 @@ error[E0622]: intrinsic must be a function --> $DIR/E0622.rs:3:5 | -LL | pub static breakpoint : unsafe extern "rust-intrinsic" fn(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a function +LL | pub static atomic_singlethreadfence_seqcst : unsafe extern "rust-intrinsic" fn(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a function error: aborting due to 1 previous error From cea0582dbdd231c46884ca917de3e32b75b6287a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 2 Dec 2024 21:15:38 -0800 Subject: [PATCH 2/2] miri: Adapt for `breakpoint` becoming safe --- src/tools/miri/tests/fail/breakpoint.rs | 4 +--- src/tools/miri/tests/fail/breakpoint.stderr | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/tests/fail/breakpoint.rs b/src/tools/miri/tests/fail/breakpoint.rs index 2dd87ea608397..42943d58191ef 100644 --- a/src/tools/miri/tests/fail/breakpoint.rs +++ b/src/tools/miri/tests/fail/breakpoint.rs @@ -1,7 +1,5 @@ #![feature(core_intrinsics)] fn main() { - unsafe { - core::intrinsics::breakpoint() //~ ERROR: trace/breakpoint trap - }; + core::intrinsics::breakpoint(); //~ ERROR: trace/breakpoint trap } diff --git a/src/tools/miri/tests/fail/breakpoint.stderr b/src/tools/miri/tests/fail/breakpoint.stderr index ca98e81f1f4ed..f203cb0c15f14 100644 --- a/src/tools/miri/tests/fail/breakpoint.stderr +++ b/src/tools/miri/tests/fail/breakpoint.stderr @@ -1,8 +1,8 @@ error: abnormal termination: trace/breakpoint trap --> tests/fail/breakpoint.rs:LL:CC | -LL | core::intrinsics::breakpoint() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trace/breakpoint trap +LL | core::intrinsics::breakpoint(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trace/breakpoint trap | = note: BACKTRACE: = note: inside `main` at tests/fail/breakpoint.rs:LL:CC