1
0
mirror of https://gitlab.gnome.org/GNOME/glib.git synced 2025-04-01 05:13:06 +02:00

gobject: Assert that GObjects are at least as aligned as basic types

See the reasoning in the patch for why we believe GObjects *are*
(already) as aligned as the basic types.

We want to make this guarantee so that it’s guaranteed to be safe for
people to ignore -Wcast-align warnings for GObjects which contain basic
types. This typically happens with gdouble on 32-bit ARM platforms.

The checks are slightly complicated by the need to support GObjects with
custom constructors. We should expect that a custom construction
function will chain up to g_object_constructor (which calls
g_type_create_instance() as normal), but it’s possible that someone has
done something crazy and uses a custom allocator which doesn’t return
with the same alignment as GSlice. Hand them a warning in that case. If
that is true, the code which uses their custom-constructed GObject can
presumably already deal with the alignment it gets given.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: 
This commit is contained in:
Philip Withnall 2017-12-08 14:34:34 +00:00 committed by Philip Withnall
parent e7e2949f31
commit 0749643daa
3 changed files with 46 additions and 0 deletions

@ -24,6 +24,8 @@
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include "../glib/glib-private.h"
#include "gobject.h" #include "gobject.h"
#include "gtype-private.h" #include "gtype-private.h"
#include "gvaluecollector.h" #include "gvaluecollector.h"
@ -1794,6 +1796,10 @@ g_object_get_type (void)
* Similarly, #gfloat is promoted to #gdouble, so you must ensure that the value * Similarly, #gfloat is promoted to #gdouble, so you must ensure that the value
* you provide is a #gdouble, even for a property of type #gfloat. * you provide is a #gdouble, even for a property of type #gfloat.
* *
* Since GLib 2.72, all #GObjects are guaranteed to be aligned to at least the
* alignment of the largest basic GLib type (typically this is #guint64 or
* #gdouble).
*
* Returns: (transfer full) (type GObject.Object): a new instance of * Returns: (transfer full) (type GObject.Object): a new instance of
* @object_type * @object_type
*/ */
@ -1816,6 +1822,26 @@ g_object_new (GType object_type,
return object; return object;
} }
/* Check alignment. (See https://gitlab.gnome.org/GNOME/glib/-/issues/1231.)
* This should never fail, since g_type_create_instance() uses g_slice_alloc0().
* The GSlice allocator always aligns to the next power of 2 greater than the
* allocation size. The allocation size for a GObject is
* sizeof(GTypeInstance) + sizeof(guint) + sizeof(GData*)
* which is 12B on 32-bit platforms, and larger on 64-bit systems. In both
* cases, thats larger than the 8B needed for a guint64 or gdouble.
*
* If GSlice falls back to malloc(), its documented to return something
* suitably aligned for any basic type. */
static inline gboolean
g_object_is_aligned (GObject *object)
{
return ((((guintptr) (void *) object) %
MAX (G_ALIGNOF (gdouble),
MAX (G_ALIGNOF (guint64),
MAX (G_ALIGNOF (gint),
G_ALIGNOF (glong))))) == 0);
}
static gpointer static gpointer
g_object_new_with_custom_constructor (GObjectClass *class, g_object_new_with_custom_constructor (GObjectClass *class,
GObjectConstructParam *params, GObjectConstructParam *params,
@ -1903,6 +1929,16 @@ g_object_new_with_custom_constructor (GObjectClass *class,
return NULL; return NULL;
} }
if (!g_object_is_aligned (object))
{
g_critical ("Custom constructor for class %s returned a non-aligned "
"GObject (which is invalid since GLib 2.72). Assuming any "
"code using this object doesnt require it to be aligned. "
"Please fix your constructor to align to the largest GLib "
"basic type (typically gdouble or guint64).",
G_OBJECT_CLASS_NAME (class));
}
/* g_object_init() will have marked the object as being in-construction. /* g_object_init() will have marked the object as being in-construction.
* Check if the returned object still is so marked, or if this is an * Check if the returned object still is so marked, or if this is an
* already-existing singleton (in which case we should not do 'constructed'). * already-existing singleton (in which case we should not do 'constructed').
@ -1969,6 +2005,8 @@ g_object_new_internal (GObjectClass *class,
object = (GObject *) g_type_create_instance (class->g_type_class.g_type); object = (GObject *) g_type_create_instance (class->g_type_class.g_type);
g_assert (g_object_is_aligned (object));
if (CLASS_HAS_PROPS (class)) if (CLASS_HAS_PROPS (class))
{ {
GSList *node; GSList *node;

@ -253,6 +253,10 @@ typedef void (*GWeakNotify) (gpointer data,
* *
* All the fields in the `GObject` structure are private to the implementation * All the fields in the `GObject` structure are private to the implementation
* and should never be accessed directly. * and should never be accessed directly.
*
* Since GLib 2.72, all #GObjects are guaranteed to be aligned to at least the
* alignment of the largest basic GLib type (typically this is #guint64 or
* #gdouble).
*/ */
struct _GObject struct _GObject
{ {

@ -1981,6 +1981,10 @@ guint g_type_get_type_registration_serial (void);
* } * }
* ]| * ]|
* *
* Since GLib 2.72, the returned `MyObjectPrivate` pointer is guaranteed to be
* aligned to at least the alignment of the largest basic GLib type (typically
* this is #guint64 or #gdouble).
*
* Note that this macro can only be used together with the `G_DEFINE_TYPE_*` * Note that this macro can only be used together with the `G_DEFINE_TYPE_*`
* macros, since it depends on variable names from those macros. * macros, since it depends on variable names from those macros.
* *