Skip to content

Issue with function alignment in const combined with is_null on platforms with function pointer value not agreeing with function alignment. #144661

Open

Description

@zachs18

I tried this code:

Running on armv5te-unknown-linux-gnueabi (and probably any other arm target with #[instruction_set(arm::t32)], or something similar on targets which use thumb mode by default, like thumbv7neon-unknown-linux-gnueabihf).

#![feature(fn_align)]
// This also occurs if using `-Zmin-function-alignment=2` on the CLI instead of `#[rustc_align(2)]` on just `foo`.

#[rustc_align(2)]
#[instruction_set(arm::t32)]
fn foo() {}

// If the assert succeeds, make sure this is the same value that `dbg!` prints below.
const EXPECTED_ADDR: usize = 0x00408aed;

const FOO_PTR: *const () = unsafe { std::mem::transmute::<fn(), *const ()>(foo) };
const MAYBE_NULL: *const () = FOO_PTR.wrapping_byte_sub(EXPECTED_ADDR);

fn main() {
    dbg!(FOO_PTR);
    let is_expected_addr_const = const { MAYBE_NULL.is_null() };
    let is_expected_addr = { MAYBE_NULL.is_null() };
    assert_eq!(is_expected_addr_const, is_expected_addr);
    println!("Hello, world!");
}

I expected to see this happen: The assert_eq succeeds (or compilation fails): the same value should not be non-null at compile time and null at runtime.

Instead, this happened:

[src/main.rs:18:5] FOO_PTR = 0x00408aed

thread 'main' panicked at src/main.rs:21:5:
assertion `left == right` failed
  left: false
 right: true
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Because foo is annotated #[rustc_align(2)], consteval believes that any odd offset from its address cannot be null. However, on ARM, all functions are aligned to at least 2, and the low bit of function pointers indicate which instruction set to use (0 = a32, 1 = t32 (thumb)). Since foo is #[instruction_set(arm::t32)], the low bit of a function pointer to it is thus odd at runtime, and thus subtracting the address it ends up at results in a null pointer.

ARM mode instructions are located on 4-byte boundaries. Thumb mode instructions are located on 2-byte boundaries. In the ARM architecture, bit 0 of a function pointer indicates the mode (ARM or Thumb) of the called function, rather than a memory location. When bit 0 = 1, Thumb mode is selected.

This also occurs if using -Zmin-function-alignment=2 on the CLI instead of #[rustc_align(2)] on just foo.

Tracking issue for fn_align: #140261

Proposed stabilization PR for -Cmin-function-alignment: #142824

Meta

rustc --version --verbose:

rustc 1.90.0-nightly (498ae9fed 2025-07-28)
binary: rustc
commit-hash: 498ae9fed2e7d90821d70a048f3770f91af08957
commit-date: 2025-07-28
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.8

I think the most "principled" solution for this would be for consteval to know about ARM (and maybe any other platform)'s function aligning/pointer tagging, so that FOO_PTR will be at offset 1 from the align-2 allocation for foo in consteval, and any arm::a32 function's function pointer would be at offset 0 from a its 4-byte-aligned allocation.

As a simpler workaround, maybe consteval could just make all function allocations be align-1 for consteval purposes, even if they have a higher alignment?

@rustbot label +A-const-eval +requires-nightly

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    on Jul 29, 2025
  2. zachs18 commented on Jul 29, 2025

    @zachs18
    ContributorAuthor

    Related discussion: rust-lang/rfcs#3806 (comment)

  3. zachs18 commented on Jul 30, 2025

    @zachs18
    ContributorAuthor

    Similar issue on wasm: cc #143368

    Running on wasm32-wasip1 under wasmtime:

    #![feature(fn_align)]
    // This also occurs if using `-Zmin-function-alignment=2` on the CLI instead of `#[rustc_align(2)]` on just `foo`.
    
    #[rustc_align(2)]
    fn foo() {}
    
    // If the assert succeeds, make sure this is the same value that `dbg!` prints below.
    const EXPECTED_ADDR: usize = 3;
    
    const FOO_PTR: *const () = unsafe { std::mem::transmute::<fn(), *const ()>(foo) };
    const MAYBE_NULL: *const () = FOO_PTR.wrapping_byte_sub(EXPECTED_ADDR);
    
    fn main() {
        dbg!(FOO_PTR);
        let is_expected_addr_const = const { MAYBE_NULL.is_null() };
        let is_expected_addr = { MAYBE_NULL.is_null() };
        assert_eq!(is_expected_addr_const, is_expected_addr);
        println!("Hello, world!");
    }
    $ cargo run
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
         Running `wasmtime run /home/zachary/opt_mount/zachary/cargo-target/wasm32-wasip1/debug/arm-thumb-fn-ptr-guaranteed-eq-testing.wasm`
    [src/main.rs:14:5] FOO_PTR = 0x00000003
    
    thread 'main' panicked at src/main.rs:17:5:
    assertion `left == right` failed
      left: false
     right: true
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    Error: failed to run main module `/home/zachary/opt_mount/zachary/cargo-target/wasm32-wasip1/debug/arm-thumb-fn-ptr-guaranteed-eq-testing.wasm`
    
    Caused by:
        0: failed to invoke command default
        1: error while executing at wasm backtrace:
               0: 0x94a0 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!abort
               1: 0x519b - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::sys::pal::wasi::helpers::abort_internal::h01b1d5dcc1f2cd52
               2: 0x2c93 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::process::abort::h81a5e10e6684ace3
               3: 0x2c89 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!__rustc[a613f0c88f7c8e84]::__rust_abort
               4: 0x1330 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!__rustc[a613f0c88f7c8e84]::__rust_start_panic
               5: 0x64fd - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!__rustc[a613f0c88f7c8e84]::rust_panic
               6: 0x6482 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::panicking::rust_panic_with_hook::h44c675780d6be893
               7: 0x554b - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::panicking::begin_panic_handler::{{closure}}::h8280227a3fd1bd9f
               8: 0x54b7 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::sys::backtrace::__rust_end_short_backtrace::h51890a6e58f2ff97
               9: 0x5e69 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!__rustc[a613f0c88f7c8e84]::rust_begin_unwind
              10: 0xa82a - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!core::panicking::panic_fmt::hf38e2205500f4ad7
              11: 0xb94f - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!core::panicking::assert_failed_inner::h24aa5b16daa6221b
              12: 0x295b - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!core::panicking::assert_failed::hd3f4140cd0f142f3
              13:  0x66c - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!arm_thumb_fn_ptr_guaranteed_eq_testing::main::h5a0d033e53eb4184
              14: 0x12b4 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!core::ops::function::FnOnce::call_once::h0185a953664c33b9
              15:  0x316 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::sys::backtrace::__rust_begin_short_backtrace::h28396c41dea34335
              16:  0x41b - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::rt::lang_start::{{closure}}::h75c4183b02724196
              17: 0x3132 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::rt::lang_start_internal::h726b5f1f3bd1ae70
              18:  0x3cd - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!std::rt::lang_start::h6502c3f054621141
              19:  0x6e0 - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!__main_void
              20:  0x2ce - arm_thumb_fn_ptr_guaranteed_eq_testing-15dedc48e8cc0343.wasm!_start
           note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information
        2: wasm trap: wasm `unreachable` instruction executed
  4. changed the title [-]Issue with function alignment in `const` combined with `is_null` and ARM function pointer tagging.[/-] [+]Issue with function alignment in `const` combined with `is_null` on platforms with function pointer alignment not agreeing with function alignment.[/+] on Jul 30, 2025
  5. asquared31415 commented on Jul 30, 2025

    @asquared31415
    Contributor

    aligning a function on wasm is even less meaningful than on platforms where metadata is in low bits, it just does not do anything IIRC

  6. changed the title [-]Issue with function alignment in `const` combined with `is_null` on platforms with function pointer alignment not agreeing with function alignment.[/-] [+]Issue with function alignment in `const` combined with `is_null` on platforms with function pointer value not agreeing with function alignment.[/+] on Jul 30, 2025
  7. added 2 commits that reference this issue on Aug 3, 2025
  8. added a commit that references this issue on Aug 4, 2025
  9. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Dec 30, 2025
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

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-bugCategory: This is a bug.F-fn_align`#![feature(fn_align)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions