edk2/MdePkg/Library/BaseLib/Unaligned.c
Ard Biesheuvel 2ba7e5be6c MdePkg/BaseLib: Avoid undefined behavior in Unaligned() accessors
The C standard qualifies misaligned pointers as undefined behavior. This
means it is fundamentally impossible to define a conformant C API for
unaligned access that is expressed in terms of types with a minimum
alignment greater than 1 byte.

Undefined behavior means that the compiler is free to generate code that
assumes that only the defined behavior occurs. This means that a C
implementation of ReadUnaligned64() might be emitted using a 64-bit load
operation that does not tolerate misalignment, depending on the CPU
architecture.

So the only correct way to define such an API is in terms of types such
as VOID* that have no implied alignment.

Then, given that compilers today are perfectly capable of generating the
right code, given accurate annotations, let's rely on those in the
generic implementation. This will ensure that the correct access
sequences are used, depending on the CPU architecture, and on the
compiler flags (e.g., AArch64 uses -mstrict-align in XIP code as it may
execute with the MMU disabled).

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
2025-12-09 08:41:08 +00:00

225 lines
5.1 KiB
C

/** @file
Unaligned access functions of BaseLib.
Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "BaseLibInternals.h"
#pragma pack(1)
typedef union {
UINT16 Val16;
UINT32 Val32;
UINT64 Val64;
} MISALIGNED;
#pragma pack()
STATIC_ASSERT (ALIGNOF (MISALIGNED) == 1, "Alignment error");
/**
Reads a 16-bit value from memory that may be unaligned.
This function returns the 16-bit value pointed to by Buffer. The function
guarantees that the read operation does not produce an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 16-bit value that may be unaligned.
@return The 16-bit value read from Buffer.
**/
UINT16
EFIAPI
ReadUnaligned16 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return ((CONST MISALIGNED *)Buffer)->Val16;
}
/**
Writes a 16-bit value to memory that may be unaligned.
This function writes the 16-bit value specified by Value to Buffer. Value is
returned. The function guarantees that the write operation does not produce
an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 16-bit value that may be unaligned.
@param Value 16-bit value to write to Buffer.
@return The 16-bit value to write to Buffer.
**/
UINT16
EFIAPI
WriteUnaligned16 (
OUT VOID *Buffer,
IN UINT16 Value
)
{
ASSERT (Buffer != NULL);
return ((MISALIGNED *)Buffer)->Val16 = Value;
}
/**
Reads a 24-bit value from memory that may be unaligned.
This function returns the 24-bit value pointed to by Buffer. The function
guarantees that the read operation does not produce an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 24-bit value that may be unaligned.
@return The 24-bit value read from Buffer.
**/
UINT32
EFIAPI
ReadUnaligned24 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return ((UINT32)((UINT8 *)Buffer)[2] << 16) | ReadUnaligned16 (Buffer);
}
/**
Writes a 24-bit value to memory that may be unaligned.
This function writes the 24-bit value specified by Value to Buffer. Value is
returned. The function guarantees that the write operation does not produce
an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 24-bit value that may be unaligned.
@param Value 24-bit value to write to Buffer.
@return The 24-bit value to write to Buffer.
**/
UINT32
EFIAPI
WriteUnaligned24 (
OUT VOID *Buffer,
IN UINT32 Value
)
{
ASSERT (Buffer != NULL);
WriteUnaligned16 (Buffer, (UINT16)Value);
((UINT8 *)Buffer)[2] = (UINT8)(Value >> 16);
return Value & 0xffffff;
}
/**
Reads a 32-bit value from memory that may be unaligned.
This function returns the 32-bit value pointed to by Buffer. The function
guarantees that the read operation does not produce an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 32-bit value that may be unaligned.
@return The 32-bit value read from Buffer.
**/
UINT32
EFIAPI
ReadUnaligned32 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return ((CONST MISALIGNED *)Buffer)->Val32;
}
/**
Writes a 32-bit value to memory that may be unaligned.
This function writes the 32-bit value specified by Value to Buffer. Value is
returned. The function guarantees that the write operation does not produce
an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 32-bit value that may be unaligned.
@param Value The 32-bit value to write to Buffer.
@return The 32-bit value to write to Buffer.
**/
UINT32
EFIAPI
WriteUnaligned32 (
OUT VOID *Buffer,
IN UINT32 Value
)
{
ASSERT (Buffer != NULL);
return ((MISALIGNED *)Buffer)->Val32 = Value;
}
/**
Reads a 64-bit value from memory that may be unaligned.
This function returns the 64-bit value pointed to by Buffer. The function
guarantees that the read operation does not produce an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 64-bit value that may be unaligned.
@return The 64-bit value read from Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return ((CONST MISALIGNED *)Buffer)->Val64;
}
/**
Writes a 64-bit value to memory that may be unaligned.
This function writes the 64-bit value specified by Value to Buffer. Value is
returned. The function guarantees that the write operation does not produce
an alignment fault.
If the Buffer is NULL, then ASSERT().
@param Buffer A pointer to a 64-bit value that may be unaligned.
@param Value The 64-bit value to write to Buffer.
@return The 64-bit value to write to Buffer.
**/
UINT64
EFIAPI
WriteUnaligned64 (
OUT VOID *Buffer,
IN UINT64 Value
)
{
ASSERT (Buffer != NULL);
return ((MISALIGNED *)Buffer)->Val64 = Value;
}