Skip to content

Issues/miscompilation around ARM T32 frame pointer with new asm syntax #73450

Closed
@cbiffle

Description

Summary

First: the new asm! syntax reserves r11 on ARM as a frame pointer. This is incorrect on Thumb targets, where it should be r7. (llvm_asm! gets this right.)

Second: if you attempt to use r7, things miscompile. (This is true of llvm_asm! as well, and is the actual issue that brought me here.)

Code

I'm having a hard time writing a minimal reproduction, because you need to create a function with sufficient complexity to trigger reliance on the frame pointer. Here's what I've got.

Imagine that we have an OS syscall, invoked on ARM by the svc instruction, that needs to receive one parameter in r7. (This is simplified from our actual ABI, but we do use r7 for [reasons].)

asm!(
    "svc #0",
    in("r7") dest.as_mut_ptr(),
    options(nostack, readonly, preserves_flags)
);

We most often see this when a function (syscall stub) containing a block like that gets inlined into another function, but we've also encountered it within a single small function.

In the disassembly, we see stuff like this:

   2c492:       f1a7 003d       sub.w   r0, r7, #61      ; <-- here r7 is acting as frame pointer
   2c496:       9019            str     r0, [sp, #100]
   2c498:       a813            add     r0, sp, #76
  ; ... unrelated instructions, but which notably do not save r7 ...
   2c4ac:       460f            mov     r7, r1   ; <-- here, r7 is being loaded with the parameter
  ; ... unrelated instructions ...
   2c4b8:       df00            svc     0    ; <-- system call!
   2c4bc:       b11c            cbz     r4, 2c4c6
   ; ... unrelated instructions ...
   2c4e0:       f1a7 003d       sub.w   r0, r7, #61     ; <-- r7 is assumed to still be frame pointer
   2c4e4:       9019            str     r0, [sp, #100]

If one were to use r11 instead (which, spoiler, we also use), one would get a compiler error:

error: invalid register `r11`: the frame pointer cannot be used as an operand for inline asm
   --> userlib/src/lib.rs:191:13
    |
191 |             in("r11") Sysnum::BorrowRead as u32,
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

...which is actually wrong for my target, but the intent is right: it appears to be trying to guard against this miscompilation case.

Expectations

I would normally expect that an explicit clobber would be enough for the compiler to not assume a particular value is live in a register across the asm! statement. This isn't true in either llvm_asm! or the new syntax. (I tried the new syntax to see if it fixed this -- and also because I expected a chilly reception to a bug in a deprecated syntax.)

Moreover, I argue that it is a misfeature to prevent the user from describing an asm sequence that damages the frame pointer. Such sequences can absolutely occur, particularly in cases of register bank switching, context restore, or naked functions that manage their own frames. The restriction on naming frame pointer should be loosened, in my opinion; the compiler can always generate code to save/restore it. (I'm currently ok with the ban on naming sp or pc.)

But barring that, the restriction should be correct.

Meta

This miscompilation was initially observed using llvm_asm! on the 2020-05-01 nightly. Confirmed with the new asm! syntax on:

rustc --version --verbose:

rustc 1.46.0-nightly (feb3536eb 2020-06-09)
binary: rustc
commit-hash: feb3536eba10c2e4585d066629598f03d5ddc7c6
commit-date: 2020-06-09
host: x86_64-unknown-linux-gnu
release: 1.46.0-nightly
LLVM version: 10.0

The asm! announcement said I should add F-asm to this report, but it is not apparent how I would do that.

Hat tip to @labbott for identifying this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    A-inline-assemblyArea: Inline assembly (`asm!(…)`)C-bugCategory: This is a bug.F-asm`#![feature(asm)]` (not `llvm_asm`)O-ArmTarget: 32-bit Arm processors (armv6, armv7, thumb...), including 64-bit Arm in AArch32 stateT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions