Skip to content

Full support for pointer fragments #72

Closed

Description

@RalfJung

These days, values of types like MaybeUninit are only ever represented in-memory in the interpreter; they cannot be stored in a Scalar. Only integers (that never have provenance) and pointers (that always have a single well-defined provenance) can exist as Scalar.

This does give us a chance to represent "partial pointers", so that we could support people writing their own memcpy in const that works by copying MaybeUninit<u8> in a loop. I don't know if there's any real usecase for this, but it seems neat. :D

The main challenge is that we have to ensure that when the bytes of a pointer get put back together, that they all appear in the right order. This is not a constraint that our current per-byte provenance system supports, and I doubt that we want to put such a constraint onto regular runtime semantics. We'd also have to report an error if such a pointer fragment byte makes it into the final value of a constant, since AFAIK we can't represent them in LLVM.

Activity

  1. added a commit that references this issue on May 4, 2025
  2. added a commit that references this issue on May 5, 2025
  3. added a commit that references this issue on Jul 17, 2025
  4. added a commit that references this issue on Aug 17, 2025
  5. added a commit that references this issue on Aug 18, 2025
  6. theemathas commented on Sep 10, 2025

    @theemathas

    This probably should be reopened due to rust-lang/rust#146324

  7. 2 remaining items

  8. added a commit that references this issue on Nov 10, 2025
  9. added a commit that references this issue on Nov 16, 2025
  10. added a commit that references this issue on Nov 30, 2025
  11. jethrogb commented on Mar 28, 2026

    @jethrogb

    I realize this is probably too much to ask, but when building low-level code, it would be very useful to compose values that consist of real pointer bits and unrelated other bits.

    For example, x86 page table entries are 64-bit values where the middle bits are part of a pointer to memory. The low bits are unused by the pointer because it's page-aligned. The high bits are unused by the pointer because the physical address space is architecturally defined to not cover the full 64 bits. The page table entry defined by the architecture therefore uses these bits for other things.

    I was able to hack together the following using inline assembly:

    pub const fn check_bits<T>(_: *const T, bits: u64) {
        let low_bits = bits & ((core::mem::align_of::<T>() - 1) as u64);
        let high_bits = bits & 0xffff_ffff_0000_0000;
        assert!((bits ^ low_bits ^ high_bits) == 0, "ptr_with_bits: bits must only have bits set below alignment and above 32 bits");
    }
    #[allow(dead_code)]
    pub struct PtrWithBits(pub *const core::ffi::c_void);
    unsafe impl Sync for PtrWithBits {}
    
    /// Create values of type `PtrWithBits` which is composed of a pointer to
    /// a real item with extra bits set in the unused parts of the pointer. The
    /// unused parts are those low bits that are always zero due to the
    /// alignment of the type, and those high bits that are unused due to 
    /// address space limitations.
    ///
    /// The current implementation assumes that the address space is 4GB.
    ///
    /// Example:
    ///
    /// ```rust,ignore
    /// unsafe_ptr_with_bits! {
    ///     const PAGE_TABLE_ENTRY: PtrWithBits = &PAGE | 3;
    /// }
    /// ```
    ///
    /// Note: this macro contains global_asm! and must be used only in places
    /// where that is legal.
    ///
    /// # Safety
    ///
    /// The caller must ensure `$p` is in the first 4GB of memory
    #[macro_export]
    macro_rules! unsafe_ptr_with_bits {
        ($(const $i:ident: PtrWithBits = &$p:path | $e:expr;)+) => {$(
            core::arch::global_asm! {
                concat!(".set ", stringify!($i), ", {p} + {o}"),
                p = sym $p,
                o = const $e as u64
            }
            const _: () = {
                $crate::x86_isa::check_bits(&MY_PAGE, $e);
            };
            const $i: $crate::x86_isa::PtrWithBits = $crate::x86_isa::PtrWithBits({
                unsafe extern "C" {
                    static $i: core::ffi::c_void;
                }
                &raw const $i
            });
        )+}
    }

    I'm compiling the code with -C code-model=medium -C relocation=static to ensure the address space is small enough. It seems to work and the compiler generates ELF relocations with the needed bits in addend.

  12. RalfJung commented on Mar 28, 2026

    @RalfJung
    MemberAuthor

    This is a closed issue, and the feature tracked here has been fully implemented. What you are asking for is something else, so please open a new issue. (Also, the example uses MY_PAGE without defining it so I am a bit confused about what you actually are trying to do.)

  13. added a commit that references this issue on Aug 31, 2026
  14. added a commit that references this issue on Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions