Re: fix alignment issue on SPARC processors
Bryce Harrington <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <[email protected]> |
On Fri, Dec 05, 2014 at 02:59:06PM +0100, Uli Schlachter wrote: > Hi Nicolas, > > Am 04.12.2014 um 20:44 schrieb Nicolas Setton: > [...] > >> I would instead suggest to add a new member "int64_t data" to the struct with a > >> suitable comment explaining why it is of type int64_t. Instead of using > >> ((unsigned char *)chunk + sizeof(*chunk)) for calculating the beginning of the > >> data area of the chunk, the code could then use (unsigned char*)&chunk->data > >> which IMO is more readable anyway. > > > > I didn't understand this suggestion. > > Could you test the attached patch? Also, this is pretty much the idea that I had > in mind. Compared to your patch, it does not add a hole to struct _pool_chunk on > 64bit platforms (in fact, nothing should change at all on 64bit). > > I basically did no testing at all on this. The test suite run on the image > backend does not crash. So it would also be nice if others could take a look at > this and spot my mistakes. Pointer arithmetic is scary. > > Cheers, > Uli > -- > "In the beginning the Universe was created. This has made a lot of > people very angry and has been widely regarded as a bad move." Inline patches -gt attachments. ;-) > [0;0m>From 9f318fc51963c221faf26b2877804d106a31b3e9 Mon Sep 17 00:00:00 2001[0;0m > [0;0mFrom: Uli Schlachter <[email protected]>[0;0m > [0;0mDate: Fri, 5 Dec 2014 14:43:26 +0100[0;0m > [0;0mSubject: [PATCH] tor-scan-converter: Correctly align 64bit types[0;0m > [0;0m[0;0m > [0;0mOn 32bit SPARC the scan converter was causing a SIGBUS due to an unaligned[0;0m > [0;0mmemory access while accessing an int64_t. This memory accessing was to struct[0;0m > [0;0mquorem's rem member.[0;0m > [0;0m[0;0m > [0;0mThis crash occurred because the rot-scan-converter contains its own[0;0m s/rot-/tor-/ > [0;0mimplementation of a memory pool. This implementation only guarantees an[0;0m > [0;0malignment of sizeof(void *), which is less than what a 64 bit type requires on[0;0m > [0;0m32bit platforms. This 4 byte alignment is guaranteed, because struct _pool_chunk[0;0m > [0;0m(which is the struct that is used for managing free space) contains elements of[0;0m > [0;0mthat size and so the size of that struct is a multiple of this size as well.[0;0m > [0;0m[0;0m > [0;0mThis problem was introduced with commit 03c3d4b7c15.[0;0m > [0;0m[0;0m > [0;0mTo fix this problem, this commit introduces a int64_t member to struct[0;0m > [0;0m_pool_chunk that marks the beginning of the free data space. Thanks to this, the[0;0m > [0;0mcompiler ensures proper alignment and sizeof(struct _pool_chunk) becomes a[0;0m > [0;0mmultiple of 8.[0;0m > [0;0m[0;0m > [0;0mHowever, previously the end of the struct marked the beginning of the data and[0;0m > [0;0msizeof() was used for correctly calculating offsets to the data section. So,[0;0m > [0;0mjust adding such a member would work, but would also waste some memory. To avoid[0;0m > [0;0mthis, this commit also changes the rest of the pool implementation to[0;0m > [0;0maccommodate.[0;0m > [0;0m[0;0m > [0;0mReported-by: Nicolas Setton <[email protected]>[0;0m > [0;0mSigned-off-by: Uli Schlachter <[email protected]>[0;0m > [0;32m---[0;0m > [0;0m src/cairo-tor-scan-converter.c | 35 ++++++++++++++++++++---------------[0;0m > [0;0m 1 file changed, 20 insertions(+), 15 deletions(-)[0;0m > [0;0m[0;0m > [0;34mdiff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c[0;0m > [0;0mindex 14922d0..7adb5ab 100644[0;0m > [0;32m--- a/src/cairo-tor-scan-converter.c[0;0m > [1;32m+++ b/src/cairo-tor-scan-converter.c[0;0m > [1;34m@@ -277,7 +277,8 @@ struct _pool_chunk {[0;0m > [0;0m * chunk in the pool header. */[0;0m > [0;0m struct _pool_chunk *prev_chunk;[0;0m > [0;0m [0;0m > [0;32m- /* Actual data starts here. Well aligned for pointers. */[0;0m > [1;32m+ /* Actual data starts here. Well aligned even for 64 bit types. */[0;0m > [1;32m+ int64_t data;[0;0m > [0;0m };[0;0m > [0;0m [0;0m > [0;0m /* A memory pool. This is supposed to be embedded on the stack or[0;0m > [1;34m@@ -299,8 +300,11 @@ struct pool {[0;0m > [0;0m [0;0m > [0;0m /* Header for the sentinel chunk. Directly following the pool[0;0m > [0;0m * struct should be some space for embedded elements from which[0;0m > [0;32m- * the sentinel chunk allocates from. */[0;0m > [0;32m- struct _pool_chunk sentinel[1];[0;0m > [1;32m+ * the sentinel chunk allocates from. This is expressed as a char[0;0m > [1;32m+ * array so that the 'int64_t data' member of _pool_chunk isn't[0;0m > [1;32m+ * included. This way embedding struct pool in other structs works[0;0m > [1;32m+ * without wasting space. */[0;0m > [1;32m+ char sentinel[sizeof(struct _pool_chunk) - sizeof(int64_t)];[0;0m > [0;0m };[0;0m > [0;0m [0;0m > [0;0m /* A polygon edge. */[0;0m > [1;34m@@ -474,8 +478,9 @@ static struct _pool_chunk *[0;0m > [0;0m _pool_chunk_create(struct pool *pool, size_t size)[0;0m > [0;0m {[0;0m > [0;0m struct _pool_chunk *p;[0;0m > [1;32m+ size_t sizeof_chunk = sizeof(struct _pool_chunk) - sizeof(int64_t);[0;0m > [0;0m [0;0m > [0;32m- p = malloc(size + sizeof(struct _pool_chunk));[0;0m > [1;32m+ p = malloc(sizeof_chunk + size);[0;0m Using sizeof_chunk is a nice cleanup, and I'd think the compiler would optimize it away, although since this same phrase is used in the sentinel declaration, maybe it's worth making it a preprocessor definition so it can be used in both places? > [0;0m if (unlikely (NULL == p))[0;0m > [0;0m longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));[0;0m > [0;0m [0;0m > [1;34m@@ -489,10 +494,10 @@ pool_init(struct pool *pool,[0;0m > [0;0m size_t embedded_capacity)[0;0m > [0;0m {[0;0m > [0;0m pool->jmp = jmp;[0;0m > [0;32m- pool->current = pool->sentinel;[0;0m > [1;32m+ pool->current = (void*) &pool->sentinel[0];[0;0m > [0;0m pool->first_free = NULL;[0;0m > [0;0m pool->default_capacity = default_capacity;[0;0m > [0;32m- _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);[0;0m > [1;32m+ _pool_chunk_init(pool->current, NULL, embedded_capacity);[0;0m > [0;0m }[0;0m > [0;0m [0;0m > [0;0m static void[0;0m > [1;34m@@ -502,7 +507,7 @@ pool_fini(struct pool *pool)[0;0m > [0;0m do {[0;0m > [0;0m while (NULL != p) {[0;0m > [0;0m struct _pool_chunk *prev = p->prev_chunk;[0;0m > [0;32m- if (p != pool->sentinel)[0;0m > [1;32m+ if (p != (void *) &pool->sentinel[0])[0;0m Is the deref/ref of sentinel just for clarity or is this actually needed for the fix. (Fine either way, I'm just curious.) > [0;0m free(p);[0;0m > [0;0m p = prev;[0;0m > [0;0m }[0;0m > [1;34m@@ -542,14 +547,14 @@ _pool_alloc_from_new_chunk([0;0m > [0;0m chunk = _pool_chunk_create (pool, capacity);[0;0m > [0;0m pool->current = chunk;[0;0m > [0;0m [0;0m > [0;32m- obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);[0;0m > [1;32m+ obj = ((unsigned char*)&chunk->data + chunk->size);[0;0m Nice, this makes it easier to grok. > [0;0m chunk->size += size;[0;0m > [0;0m return obj;[0;0m > [0;0m }[0;0m > [0;0m [0;0m > [0;0m /* Allocate size bytes from the pool. The first allocated address[0;0m > [0;32m- * returned from a pool is aligned to sizeof(void*). Subsequent[0;0m > [0;32m- * addresses will maintain alignment as long as multiples of void* are[0;0m > [1;32m+ * returned from a pool is aligned to 8 bytes. Subsequent[0;0m > [1;32m+ * addresses will maintain alignment as long as multiples of 8 are[0;0m > [0;0m * allocated. Returns the address of a new memory area or %NULL on[0;0m > [0;0m * allocation failures. The pool retains ownership of the returned[0;0m > [0;0m * memory. */[0;0m > [1;34m@@ -559,7 +564,7 @@ pool_alloc (struct pool *pool, size_t size)[0;0m > [0;0m struct _pool_chunk *chunk = pool->current;[0;0m > [0;0m [0;0m > [0;0m if (size <= chunk->capacity - chunk->size) {[0;0m > [0;32m- void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);[0;0m > [1;32m+ void *obj = ((unsigned char*)&chunk->data + chunk->size);[0;0m > [0;0m chunk->size += size;[0;0m > [0;0m return obj;[0;0m > [0;0m } else {[0;0m > [1;34m@@ -573,16 +578,16 @@ pool_reset (struct pool *pool)[0;0m > [0;0m {[0;0m > [0;0m /* Transfer all used chunks to the chunk free list. */[0;0m > [0;0m struct _pool_chunk *chunk = pool->current;[0;0m > [0;32m- if (chunk != pool->sentinel) {[0;0m > [0;32m- while (chunk->prev_chunk != pool->sentinel) {[0;0m > [1;32m+ if (chunk != (void *) &pool->sentinel[0]) {[0;0m > [1;32m+ while (chunk->prev_chunk != (void *) &pool->sentinel[0]) {[0;0m > [0;0m chunk = chunk->prev_chunk;[0;0m > [0;0m }[0;0m > [0;0m chunk->prev_chunk = pool->first_free;[0;0m > [0;0m pool->first_free = pool->current;[0;0m > [0;0m }[0;0m > [0;0m /* Reset the sentinel as the current chunk. */[0;0m > [0;32m- pool->current = pool->sentinel;[0;0m > [0;32m- pool->sentinel->size = 0;[0;0m > [1;32m+ pool->current = (void *) &pool->sentinel[0];[0;0m > [1;32m+ pool->current->size = 0;[0;0m This is intentional to set the current chunk's size to 0? Assuming it is, Reviewed-by: Bryce Harrington <[email protected]> > [0;0m }[0;0m > [0;0m [0;0m > [0;0m /* Rewinds the cell list's cursor to the beginning. After rewinding[0;0m > [0;32m-- [0;0m > [0;0m2.1.3[0;0m > [0;0m[0;0m -- cairo mailing list [email protected] http://lists.cairographics.org/mailman/listinfo/cairo