bfs: fix misaligned access

The BFS on-disk data is not aligned. Reading it to memory and trying to
access fields directly does not work on sparc. memcpy the data to an
aligned variable before handling it with its native size.

gcc knows how to access unaligned data, but we need to tell it when to do
so. This is done with the "packed" attribute, but it works only on
structs. So we have to wrap the values in a struct.

Thanks to C++ features, we can make the struct
relatively transparent by having an assigment operator (for writes) and
a cast operator (for read access), so there is no need to access the
value inside the struct with ".value" everywhere. The rest of the code
is then largely unchanged (except for use in printf statements and other
vararg functions, where the implicit casting can't work).

gcc takes care of performing the access in the correct way on platforms
that need it (old ARM, sparc) and can still optimize things on other
architectures where specific code is not needed for unaligned access.

Fixes #9255

Change-Id: I3bf62590dee059ad32b1845bdc4eace165b73203
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2363
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
PulkoMandy
2021-01-05 18:51:49 +00:00
committed by Adrien Destugues
parent b81d67ed0c
commit 60f8e54f2b
2 changed files with 22 additions and 9 deletions
@@ -1055,7 +1055,7 @@ BPlusTree::_FindKey(const bplustree_node* node, const uint8* key,
return B_ENTRY_NOT_FOUND;
}
off_t* values = node->Values();
Unaligned<off_t>* values = node->Values();
int16 saveIndex = -1;
// binary search in the key array
@@ -2934,7 +2934,7 @@ bplustree_node::KeyAt(int32 index, uint16* keyLength) const
return NULL;
uint8* keyStart = Keys();
uint16* keyLengths = KeyLengths();
Unaligned<uint16>* keyLengths = KeyLengths();
*keyLength = BFS_ENDIAN_TO_HOST_INT16(keyLengths[index])
- (index != 0 ? BFS_ENDIAN_TO_HOST_INT16(keyLengths[index - 1]) : 0);
@@ -3008,7 +3008,7 @@ bplustree_node::CheckIntegrity(uint32 nodeSize) const
}
if (Values()[i] == -1) {
dprintf("invalid node %p, value %d: %" B_PRIdOFF ": values "
"corrupted\n", this, (int)i, Values()[i]);
"corrupted\n", this, (int)i, Values()[i].value);
return B_BAD_DATA;
}
}
@@ -74,6 +74,18 @@ enum bplustree_types {
struct duplicate_array;
template <typename T>
struct __attribute__((packed)) Unaligned {
T value;
Unaligned<T>& operator=(const T& newValue)
{
value = newValue; return *this;
}
operator T() const { return value; }
};
struct bplustree_node {
int64 left_link;
int64 right_link;
@@ -97,8 +109,8 @@ struct bplustree_node {
{ return BFS_ENDIAN_TO_HOST_INT16(
all_key_length); }
inline uint16* KeyLengths() const;
inline off_t* Values() const;
inline Unaligned<uint16>* KeyLengths() const;
inline Unaligned<off_t>* Values() const;
inline uint8* Keys() const;
inline int32 Used() const;
uint8* KeyAt(int32 index, uint16* keyLength) const;
@@ -559,18 +571,19 @@ bplustree_header::IsValidLink(off_t link) const
// #pragma mark - bplustree_node inline functions
inline uint16*
inline Unaligned<uint16>*
bplustree_node::KeyLengths() const
{
return (uint16*)(((char*)this) + key_align(sizeof(bplustree_node)
return (Unaligned<uint16>*)(((char*)this) + key_align(sizeof(bplustree_node)
+ AllKeyLength()));
}
inline off_t*
inline Unaligned<off_t>*
bplustree_node::Values() const
{
return (off_t*)((char*)KeyLengths() + NumKeys() * sizeof(uint16));
return (Unaligned<off_t>*)(
(char*)KeyLengths() + NumKeys() * sizeof(uint16));
}