Re: fix alignment issue on SPARC processors

Nicolas Setton <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
Hello Uli,

thank you for the feedback.

>> There is an alignment issue on SPARC processors which causes a SIGBUS when
>> trying to write data to edges in the polygon scan converter.
> 
> Could you provide some more information about this? I guess you are on 32bit
> SPARC since otherwise all members of struct _pool_chunk already be 64bit in size.

Right, this is 32bit SPARC (Solaris 10).

> So size_t is a 32 bit type for you and the prev_chunk member thus starts aligned
> to a 64 bit boundary for you. Since it is a 32 bit pointer, the end of the
> struct is no longer 32bit-aligned. You fix this by adding a 16-bit member in
> front of prev_chunk so that (plus the 16-bit hole that this forces) the end of
> struct becomes 64-bit aligned.
> 
> Did I understand this correctly so far?

Right, see below.

> Also, where exactly does the SIGBUS happen? I guess that you are refering to the
> int64_t dy member of struct edge which e.g. polygon_add_edge() allocates from
> some pool via pool_alloc(). So if the struct itself isn't aligned to a 64 bit
> boundary, the dy member isn't either.


The SIGBUS happens when writing to edge->dxdy.rem, which is a long long.

Here is a reproducer:

int main(void) {
  struct polygon * polygon;
  struct edge * e;

  polygon = (struct polygon *) malloc(sizeof (struct polygon));
  polygon_init (polygon, NULL);

  e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));

  e->dxdy.quo = 42;   /* this works */
  e->dxdy.rem = 42;   /* SIGBUS here */

  /* sanity check */
  printf("%d\n", e->dxdy.quo);
  printf("%d\n", e->dxdy.rem);
  return 0;
} 

There are two cases that I could find where the pointer arithmetic
is in disagreement with the alignment rule.

In pool_alloc, we have this:

    struct _pool_chunk *chunk = pool->current;

    if (size <= chunk->capacity - chunk->size) {
        void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
        chunk->size += size;
        return obj;

and we have the equivalent in _pool_alloc_from_new_chunk:

    obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);

We have sizeof(*chunk) = 12 on both cases, so obj->dxdy.rem is
not aligned after this addition.

Bumping sizeof(*chunk) to a multiple of 8 circumvents the problem in 
both cases.

> It would be nice if you submit your patch in the format produced by git
> format-patch. That includes a commit message and tracks you as the author of
> this, instead of someone else claiming the fame for this. All of the above
> reasoning and thoughts about this change could go into the commit message.

Attached. I am also happy with someone else suggesting a better patch and 
claiming the fame for this.

> The big downside of this: On 64bit platforms this adds an unused 64 bit hole to
> the struct.

Right, although my understanding of this part is that these are ephemeral, and
we want to privilege speed over memory consumption.

> 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.

> However, I guess that such a change is more complicated since it requires
> reviewing the code more carefully since now sizeof(*chunk) is no longer the size
> of the "chunk header"...

Right. Perhaps we could also pre-allocate the array (embedded) as part of the pool
rather than a separate variable after the end of the pool, and keep an index on the
first free element of the array.

Nicolas

-- 
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
0001-Pad-against-alignment-issue.patch (application/octet-stream, 1.1 KB)
From edd1914967c57ad08c1bf14b1537d05a5803f289 Mon Sep 17 00:00:00 2001
From: Nicolas Setton <[email protected]>
Date: Thu, 4 Dec 2014 13:09:00 -0500
Subject: [PATCH] Pad against alignment issue

Add padding to _pool_chunk to make the size of the struct
a multiple of 8 bytes, to prevent a sigbus on 32-bit SPARC
processors where the alignment for long long is strictly 64
bits.
---
 src/cairo-tor-scan-converter.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 14922d0..d811825 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -273,6 +273,11 @@ struct _pool_chunk {
     /* # bytes total in this chunk */
     size_t capacity;
 
+    /* Pad to fix alignment issue on sparc: the long long integers
+     * are aligned on 64-bit boundaries. Make sure that this is the
+     * case in the memory pool by bumping the size of this struct. */
+    short padding;
+
     /* Pointer to the previous chunk or %NULL if this is the sentinel
      * chunk in the pool header. */
     struct _pool_chunk *prev_chunk;
-- 
1.9.3 (Apple Git-50)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.