libc/minimal: fix realloc() allocated memory alignment
The definition for realloc() says that it should return a pointer to the allocated memory which is suitably aligned for any built-in type. Turn sys_heap_realloc() into a sys_heap_aligned_realloc() and use it with __alignof__(z_max_align_t) to implement realloc() with proper memory alignment for any platform. Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
This commit is contained in:
parent
4690b8d5ec
commit
c822e0abbd
4 changed files with 62 additions and 18 deletions
|
@ -137,10 +137,15 @@ void sys_heap_free(struct sys_heap *h, void *mem);
|
|||
*
|
||||
* @param heap Heap from which to allocate
|
||||
* @param ptr Original pointer returned from a previous allocation
|
||||
* @param align Alignment in bytes, must be a power of two
|
||||
* @param bytes Number of bytes requested for the new block
|
||||
* @return Pointer to memory the caller can now use, or NULL
|
||||
*/
|
||||
void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes);
|
||||
void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
|
||||
size_t align, size_t bytes);
|
||||
|
||||
#define sys_heap_realloc(heap, ptr, bytes) \
|
||||
sys_heap_aligned_realloc(heap, ptr, 0, bytes)
|
||||
|
||||
/** @brief Validate heap integrity
|
||||
*
|
||||
|
|
|
@ -56,7 +56,9 @@ static int malloc_prepare(const struct device *unused)
|
|||
|
||||
void *realloc(void *ptr, size_t requested_size)
|
||||
{
|
||||
void *ret = sys_heap_realloc(&z_malloc_heap, ptr, requested_size);
|
||||
void *ret = sys_heap_aligned_realloc(&z_malloc_heap, ptr,
|
||||
__alignof__(z_max_align_t),
|
||||
requested_size);
|
||||
|
||||
return ret == NULL ? ptr : ret;
|
||||
}
|
||||
|
|
|
@ -312,28 +312,34 @@ void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes)
|
|||
return mem;
|
||||
}
|
||||
|
||||
void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
|
||||
void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
|
||||
size_t align, size_t bytes)
|
||||
{
|
||||
struct z_heap *h = heap->heap;
|
||||
|
||||
/* special realloc semantics */
|
||||
if (ptr == NULL) {
|
||||
return sys_heap_alloc(heap, bytes);
|
||||
return sys_heap_aligned_alloc(heap, align, bytes);
|
||||
}
|
||||
if (bytes == 0) {
|
||||
sys_heap_free(heap, ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__ASSERT((align & (align - 1)) == 0, "align must be a power of 2");
|
||||
|
||||
if (size_too_big(h, bytes)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chunkid_t c = mem_to_chunkid(h, ptr);
|
||||
chunkid_t rc = right_chunk(h, c);
|
||||
size_t chunks_need = bytes_to_chunksz(h, bytes);
|
||||
size_t align_gap = (uint8_t *)ptr - (uint8_t *)chunk_mem(h, c);
|
||||
size_t chunks_need = bytes_to_chunksz(h, bytes + align_gap);
|
||||
|
||||
if (chunk_size(h, c) == chunks_need) {
|
||||
if (align && ((uintptr_t)ptr & (align - 1))) {
|
||||
/* ptr is not sufficiently aligned */
|
||||
} else if (chunk_size(h, c) == chunks_need) {
|
||||
/* We're good already */
|
||||
return ptr;
|
||||
} else if (chunk_size(h, c) > chunks_need) {
|
||||
|
@ -357,9 +363,10 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
|
|||
merge_chunks(h, c, rc);
|
||||
set_chunk_used(h, c, true);
|
||||
return ptr;
|
||||
} else {
|
||||
/* Reallocate and copy */
|
||||
void *ptr2 = sys_heap_alloc(heap, bytes);
|
||||
}
|
||||
|
||||
/* Fallback: allocate and copy */
|
||||
void *ptr2 = sys_heap_aligned_alloc(heap, align, bytes);
|
||||
|
||||
if (ptr2 == NULL) {
|
||||
return NULL;
|
||||
|
@ -368,7 +375,6 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
|
|||
memcpy(ptr2, ptr, bytes);
|
||||
sys_heap_free(heap, ptr);
|
||||
return ptr2;
|
||||
}
|
||||
}
|
||||
|
||||
void sys_heap_init(struct sys_heap *heap, void *mem, size_t bytes)
|
||||
|
|
|
@ -261,7 +261,7 @@ static void test_realloc(void)
|
|||
|
||||
zassert_true(sys_heap_validate(&heap), "invalid heap");
|
||||
zassert_true(p1 != p2,
|
||||
"Realloc should must have moved %p", p1);
|
||||
"Realloc should have moved %p", p1);
|
||||
zassert_true(realloc_check_block(p2, p2, 64), "data changed");
|
||||
zassert_true(realloc_check_block(p3, p1, 64), "data changed");
|
||||
|
||||
|
@ -290,6 +290,37 @@ static void test_realloc(void)
|
|||
"Realloc should have expanded in place %p -> %p",
|
||||
p1, p3);
|
||||
zassert_true(realloc_check_block(p3, p1, 61), "data changed");
|
||||
|
||||
/* Corner case with sys_heap_aligned_realloc() on 32-bit targets
|
||||
* where actual memory doesn't match with given pointer
|
||||
* (align_gap != 0).
|
||||
*/
|
||||
p1 = sys_heap_aligned_alloc(&heap, 8, 32);
|
||||
realloc_fill_block(p1, 32);
|
||||
p2 = sys_heap_alloc(&heap, 32);
|
||||
realloc_fill_block(p2, 32);
|
||||
p3 = sys_heap_aligned_realloc(&heap, p1, 8, 36);
|
||||
|
||||
zassert_true(sys_heap_validate(&heap), "invalid heap");
|
||||
zassert_true(realloc_check_block(p3, p1, 32), "data changed");
|
||||
zassert_true(realloc_check_block(p2, p2, 32), "data changed");
|
||||
realloc_fill_block(p3, 36);
|
||||
zassert_true(sys_heap_validate(&heap), "invalid heap");
|
||||
zassert_true(p1 != p3,
|
||||
"Realloc should have moved %p", p1);
|
||||
|
||||
/* Test realloc with increasing alignment */
|
||||
p1 = sys_heap_aligned_alloc(&heap, 32, 32);
|
||||
p2 = sys_heap_aligned_alloc(&heap, 8, 32);
|
||||
p3 = sys_heap_aligned_realloc(&heap, p2, 8, 16);
|
||||
zassert_true(sys_heap_validate(&heap), "invalid heap");
|
||||
zassert_true(p2 == p3,
|
||||
"Realloc should have expanded in place %p -> %p",
|
||||
p2, p3);
|
||||
p3 = sys_heap_aligned_alloc(&heap, 32, 8);
|
||||
zassert_true(sys_heap_validate(&heap), "invalid heap");
|
||||
zassert_true(p2 != p3,
|
||||
"Realloc should have moved %p", p2);
|
||||
}
|
||||
|
||||
void test_main(void)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue