dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.98k stars 4.66k forks source link

Unsafe.ReadUnaligned / WriteUnaligned cause Unhandled fault: alignment exception on armhf #80068

Open kekekeks opened 1 year ago

kekekeks commented 1 year ago

Description

It seems that ReadUnaligned and WriteUnaligned no longer support unaligned pointer access on ARM architecture.

Reproduction Steps

https://godbolt.org/z/nGh9vP6KK

using System;
using System.Runtime.CompilerServices;

unsafe class Repro
{
    public struct Test
    {
        public int X, Y, Z;
    }

    public static Test Test1(void*ptr)
    {
        return Unsafe.ReadUnaligned<Test>(ptr);
    }

    public static Test Test2(void*ptr)
    {
        return *(Test*)ptr;
    }

    public static void WTest1(void*ptr, Test t)
    {
        Unsafe.WriteUnaligned<Test>(ptr, t);
    }

    public static void WTest2(void*ptr, Test t)
    {
        *(Test*)ptr = t;
    }
}

.NET 6.0: Only ReadUnaligned generates invalid code

Repro:WTest2(int,Repro+Test):
            push    {r1,r2,r3}
            push    {lr}
            ldr     r3, [sp+0x04]
            str     r3, [r0]
            ldr     r3, [sp+0x08]
            str     r3, [r0+4]
            ldr     r3, [sp+0x0c]
            str     r3, [r0+8]
            pop     lr
            add     sp, 12
            bx      lr

Repro:WTest1(int,Repro+Test):
            push    {r1,r2,r3}
            push    {lr}
            sub     sp, 16
            ldr     r3, [sp+0x14]
            str     r3, [sp+0x04]   // [V04 tmp2]
            ldr     r3, [sp+0x18]   // [V01 arg1+0x04]
            str     r3, [sp+0x08]   // [V05 tmp3]
            ldr     r3, [sp+0x1c]   // [V01 arg1+0x08]
            str     r3, [sp+0x0c]   // [V06 tmp4]
            ldr     r3, [sp+0x04]   // [V03 tmp1]
            str     r3, [r0]
            ldr     r3, [sp+0x08]   // [V03 tmp1+0x04]
            str     r3, [r0+4]
            ldr     r3, [sp+0x0c]   // [V03 tmp1+0x08]
            str     r3, [r0+8]
            add     sp, 16
            pop     lr
            add     sp, 12
            bx      lr

Repro:Test2(int):Repro+Test:
            push    {r3,lr}
            ldr     r3, [r1]
            str     r3, [r0]
            ldr     r3, [r1+4]
            str     r3, [r0+4]
            ldr     r3, [r1+8]
            str     r3, [r0+8]
            pop     {r3,pc}

Repro:Test1(int):Repro+Test:
            push    {r3,lr}
            ldr     r3, [r1]
            str     r3, [r0]
            ldr     r3, [r1+4]
            str     r3, [r0+4]
            ldr     r3, [r1+8]
            str     r3, [r0+8]
            pop     {r3,pc}

.NET 7.0: Both WriteUnaligned and ReadUnaligned generate direct pointer access

Repro:Test1(uint):Test:
            push    {r3,lr}
            ldr     r3, [r1]
            str     r3, [r0]
            ldr     r3, [r1+0x04]
            str     r3, [r0+0x04]
            ldr     r3, [r1+0x08]
            str     r3, [r0+0x08]
            pop     {r3,pc}

Repro:Test2(uint):Test:
            push    {r3,lr}
            ldr     r3, [r1]
            str     r3, [r0]
            ldr     r3, [r1+0x04]
            str     r3, [r0+0x04]
            ldr     r3, [r1+0x08]
            str     r3, [r0+0x08]
            pop     {r3,pc}

Repro:WTest1(uint,Test):
            push    {r1,r2,r3}
            push    {lr}
            ldr     r3, [sp+0x04]
            str     r3, [r0]
            ldr     r3, [sp+0x08]
            str     r3, [r0+0x04]
            ldr     r3, [sp+0x0C]
            str     r3, [r0+0x08]
            pop     lr
            add     sp, 12
            bx      lr

Repro:WTest2(uint,Test):
            push    {r1,r2,r3}
            push    {lr}
            ldr     r3, [sp+0x04]
            str     r3, [r0]
            ldr     r3, [sp+0x08]
            str     r3, [r0+0x04]
            ldr     r3, [sp+0x0C]
            str     r3, [r0+0x08]
            pop     lr
            add     sp, 12
            bx      lr

Expected behavior

Generated code properly handles misaligned pointers

Actual behavior

Unhandled fault: alignment exception

Regression?

No response

Known Workarounds

Buffer.MemoryCopy

Configuration

Raspberry Pi 3, Debian 11 32 bit

Other information

No response

dotnet-issue-labeler[bot] commented 1 year ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

ghost commented 1 year ago

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch, @kunalspathak See info in area-owners.md if you want to be subscribed.

Issue Details
### Description It seems that ReadUnaligned and WriteUnaligned no longer support unaligned pointer access on ARM architecture. ### Reproduction Steps https://godbolt.org/z/nGh9vP6KK ``` using System; using System.Runtime.CompilerServices; unsafe class Repro { public struct Test { public int X, Y, Z; } public static Test Test1(void*ptr) { return Unsafe.ReadUnaligned(ptr); } public static Test Test2(void*ptr) { return *(Test*)ptr; } public static void WTest1(void*ptr, Test t) { Unsafe.WriteUnaligned(ptr, t); } public static void WTest2(void*ptr, Test t) { *(Test*)ptr = t; } } ``` .NET 6.0: Only ReadUnaligned generates invalid code ``` Repro:WTest2(int,Repro+Test): push {r1,r2,r3} push {lr} ldr r3, [sp+0x04] str r3, [r0] ldr r3, [sp+0x08] str r3, [r0+4] ldr r3, [sp+0x0c] str r3, [r0+8] pop lr add sp, 12 bx lr Repro:WTest1(int,Repro+Test): push {r1,r2,r3} push {lr} sub sp, 16 ldr r3, [sp+0x14] str r3, [sp+0x04] // [V04 tmp2] ldr r3, [sp+0x18] // [V01 arg1+0x04] str r3, [sp+0x08] // [V05 tmp3] ldr r3, [sp+0x1c] // [V01 arg1+0x08] str r3, [sp+0x0c] // [V06 tmp4] ldr r3, [sp+0x04] // [V03 tmp1] str r3, [r0] ldr r3, [sp+0x08] // [V03 tmp1+0x04] str r3, [r0+4] ldr r3, [sp+0x0c] // [V03 tmp1+0x08] str r3, [r0+8] add sp, 16 pop lr add sp, 12 bx lr Repro:Test2(int):Repro+Test: push {r3,lr} ldr r3, [r1] str r3, [r0] ldr r3, [r1+4] str r3, [r0+4] ldr r3, [r1+8] str r3, [r0+8] pop {r3,pc} Repro:Test1(int):Repro+Test: push {r3,lr} ldr r3, [r1] str r3, [r0] ldr r3, [r1+4] str r3, [r0+4] ldr r3, [r1+8] str r3, [r0+8] pop {r3,pc} ``` .NET 7.0: Both WriteUnaligned and ReadUnaligned generate direct pointer access ``` Repro:Test1(uint):Test: push {r3,lr} ldr r3, [r1] str r3, [r0] ldr r3, [r1+0x04] str r3, [r0+0x04] ldr r3, [r1+0x08] str r3, [r0+0x08] pop {r3,pc} Repro:Test2(uint):Test: push {r3,lr} ldr r3, [r1] str r3, [r0] ldr r3, [r1+0x04] str r3, [r0+0x04] ldr r3, [r1+0x08] str r3, [r0+0x08] pop {r3,pc} Repro:WTest1(uint,Test): push {r1,r2,r3} push {lr} ldr r3, [sp+0x04] str r3, [r0] ldr r3, [sp+0x08] str r3, [r0+0x04] ldr r3, [sp+0x0C] str r3, [r0+0x08] pop lr add sp, 12 bx lr Repro:WTest2(uint,Test): push {r1,r2,r3} push {lr} ldr r3, [sp+0x04] str r3, [r0] ldr r3, [sp+0x08] str r3, [r0+0x04] ldr r3, [sp+0x0C] str r3, [r0+0x08] pop lr add sp, 12 bx lr ``` ### Expected behavior Generated code properly handles misaligned pointers ### Actual behavior `Unhandled fault: alignment exception` ### Regression? _No response_ ### Known Workarounds Buffer.MemoryCopy ### Configuration Raspberry Pi 3, Debian 11 32 bit ### Other information _No response_
Author: kekekeks
Assignees: -
Labels: `arch-arm32`, `area-CodeGen-coreclr`, `untriaged`
Milestone: -
kekekeks commented 1 year ago

Unsafe.CopyBlockUnaligned doesn't seem to respect memory alignment restrictions either when used with Unsafe.SizeOf<T>

JulieLeeMSFT commented 1 year ago

@BruceForstall PTAL.

EgorBo commented 1 year ago

I'm not 100% sure I know how we detect "this platform doesn't support unaligned reads" so it can be a bug in that logic that detects it?

Another issue is what we assume in R2R? Do we throw R2R unconditionally if current platform doesn't support them or R2R itself assumes unaligned reads are not allowed on given platform?

jkotas commented 1 year ago

The Linux distros that we test on have the alignment fixups enabled by default. There are likely number of other issues that would popup with alignment fixups disabled.

Does your distro have the alignment fixups explicitly disabled? Can you enable them?

Or have you encountered this issue when accessing device memory (similar to https://github.com/dotnet/runtime/issues/66082)?

jakobbotsch commented 1 year ago

.NET 6.0: Only ReadUnaligned generates invalid code

I'm confused, I see the exact same stores/loads from the pointer (in r0) in all your examples, only there are some extra unnecessary copies between stack slots that we are now getting rid of in .NET 7. I do believe that the JIT always assumes that unaligned integer loads/stores are supported by the target, so unaligned loads/stores are only treated specially for floats.

kekekeks commented 1 year ago

@jkotas the issue was reported on 32-bit Raspbian / Raspberry Pi Zero 2. The memory was allocated via Marshal.AllocHGlobal. Replacing ReadUnaligned with manual byte copy loop fixed the issue.

kekekeks commented 1 year ago

@jakobbotsch we do read double, float and float-based System.Numerics structures via ReadUnaligned