glib/gtypelib.c
Colin Walters bfb6271201 Strengthen check for interface vs basic types
Previously we were just checking "reserved", but to be more correct
we should check whether reserved2 is zero as well because it is
possible for offsets to have reserved = 0 but reserved2 != 0.

svn path=/trunk/; revision=620
2008-09-19 23:43:38 +00:00

2158 lines
50 KiB
C

/* GObject introspection: typelib validation, auxiliary functions
* related to the binary typelib format
*
* Copyright (C) 2005 Matthias Clasen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "config.h"
#include "gtypelib.h"
typedef struct {
GTypelib *typelib;
GSList *context_stack;
} ValidateContext;
#define ALIGN_VALUE(this, boundary) \
(( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
static void
push_context (ValidateContext *ctx, const char *name)
{
ctx->context_stack = g_slist_prepend (ctx->context_stack, (char*)name);
}
static void
pop_context (ValidateContext *ctx)
{
g_assert (ctx->context_stack != NULL);
ctx->context_stack = g_slist_delete_link (ctx->context_stack,
ctx->context_stack);
}
static gboolean
validate_interface_blob (ValidateContext *ctx,
guint32 offset,
GError **error);
DirEntry *
get_dir_entry_checked (GTypelib *typelib,
guint16 index,
GError **error)
{
Header *header = (Header *)typelib->data;
guint32 offset;
if (index == 0 || index > header->n_entries)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid directory index %d", index);
return FALSE;
}
offset = header->directory + (index - 1) * header->entry_blob_size;
if (typelib->len < offset + sizeof (DirEntry))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
return (DirEntry *)&typelib->data[offset];
}
static CommonBlob *
get_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
if (typelib->len < offset + sizeof (CommonBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
return (CommonBlob *)&typelib->data[offset];
}
static InterfaceTypeBlob *
get_type_blob (GTypelib *typelib,
SimpleTypeBlob *simple,
GError **error)
{
if (simple->offset == 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"Expected blob for type");
return FALSE;
}
if (simple->reserved == 0 && simple->reserved2 == 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"Expected non-basic type but got %d",
simple->tag);
return FALSE;
}
return (InterfaceTypeBlob*) get_blob (typelib, simple->offset, error);
}
DirEntry *
g_typelib_get_dir_entry (GTypelib *typelib,
guint16 index)
{
Header *header = (Header *)typelib->data;
return (DirEntry *)&typelib->data[header->directory + (index - 1) * header->entry_blob_size];
}
void
g_typelib_check_sanity (void)
{
/* Check that struct layout is as we expect */
gboolean size_check_ok = TRUE;
#define CHECK_SIZE(s,n) \
if (sizeof(s) != n) \
{ \
g_printerr ("sizeof("#s") is expected to be %d but is %"G_GSIZE_FORMAT".\n", \
n, sizeof (s)); \
size_check_ok = FALSE; \
}
CHECK_SIZE (Header, 104);
CHECK_SIZE (DirEntry, 12);
CHECK_SIZE (SimpleTypeBlob, 4);
CHECK_SIZE (ArgBlob, 12);
CHECK_SIZE (SignatureBlob, 8);
CHECK_SIZE (CommonBlob, 8);
CHECK_SIZE (FunctionBlob, 16);
CHECK_SIZE (InterfaceTypeBlob, 4);
CHECK_SIZE (ArrayTypeBlob, 8);
CHECK_SIZE (ParamTypeBlob, 4);
CHECK_SIZE (ErrorTypeBlob, 4);
CHECK_SIZE (ErrorDomainBlob, 16);
CHECK_SIZE (ValueBlob, 12);
CHECK_SIZE (FieldBlob, 12);
CHECK_SIZE (RegisteredTypeBlob, 16);
CHECK_SIZE (StructBlob, 20);
CHECK_SIZE (EnumBlob, 20);
CHECK_SIZE (PropertyBlob, 12);
CHECK_SIZE (SignalBlob, 12);
CHECK_SIZE (VFuncBlob, 16);
CHECK_SIZE (ObjectBlob, 32);
CHECK_SIZE (InterfaceBlob, 28);
CHECK_SIZE (ConstantBlob, 20);
CHECK_SIZE (AnnotationBlob, 12);
CHECK_SIZE (UnionBlob, 28);
#undef CHECK_SIZE
g_assert (size_check_ok);
}
static gboolean
is_aligned (guint32 offset)
{
return offset == ALIGN_VALUE (offset, 4);
}
#define MAX_NAME_LEN 200
static const char *
get_string (GTypelib *typelib, guint32 offset, GError **error)
{
if (typelib->len < offset)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"Buffer is too short while looking up name");
return NULL;
}
return (const char*)&typelib->data[offset];
}
static const char *
get_string_nofail (GTypelib *typelib, guint32 offset)
{
const char *ret = get_string (typelib, offset, NULL);
g_assert (ret);
return ret;
}
static gboolean
validate_name (GTypelib *typelib,
const char *msg,
const guchar *data, guint32 offset,
GError **error)
{
const char *name;
name = get_string (typelib, offset, error);
if (!name)
return FALSE;
if (!memchr (name, '\0', MAX_NAME_LEN))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The %s is too long: %s",
msg, name);
return FALSE;
}
if (strspn (name, G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_") < strlen (name))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The %s is contains invalid characters: %s",
msg, name);
return FALSE;
}
return TRUE;
}
static gboolean
validate_header (ValidateContext *ctx,
GError **error)
{
GTypelib *typelib = ctx->typelib;
Header *header;
if (typelib->len < sizeof (Header))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
header = (Header *)typelib->data;
if (strncmp (header->magic, G_IR_MAGIC, 16) != 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Magic string not found");
return FALSE;
}
if (header->major_version != 1 || header->minor_version != 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Version mismatch");
return FALSE;
}
if (header->n_entries < header->n_local_entries)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Inconsistent entry counts");
return FALSE;
}
if (header->size != typelib->len)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Typelib size mismatch");
return FALSE;
}
if (header->entry_blob_size != 12 ||
header->function_blob_size != 16 ||
header->callback_blob_size != 12 ||
header->signal_blob_size != 12 ||
header->vfunc_blob_size != 16 ||
header->arg_blob_size != 12 ||
header->property_blob_size != 12 ||
header->field_blob_size != 12 ||
header->value_blob_size != 12 ||
header->constant_blob_size != 20 ||
header->error_domain_blob_size != 16 ||
header->annotation_blob_size != 12 ||
header->signature_blob_size != 8 ||
header->enum_blob_size != 20 ||
header->struct_blob_size != 20 ||
header->object_blob_size != 32 ||
header->interface_blob_size != 28 ||
header->union_blob_size != 28)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Blob size mismatch");
return FALSE;
}
if (!is_aligned (header->directory))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Misaligned directory");
return FALSE;
}
if (!is_aligned (header->annotations))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Misaligned annotations");
return FALSE;
}
if (header->annotations == 0 && header->n_annotations > 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
"Wrong number of annotations");
return FALSE;
}
if (!validate_name (typelib, "namespace", typelib->data, header->namespace, error))
return FALSE;
return TRUE;
}
static gboolean validate_type_blob (GTypelib *typelib,
guint32 offset,
guint32 signature_offset,
gboolean return_type,
GError **error);
static gboolean
validate_array_type_blob (GTypelib *typelib,
guint32 offset,
guint32 signature_offset,
gboolean return_type,
GError **error)
{
ArrayTypeBlob *blob;
blob = (ArrayTypeBlob*)&typelib->data[offset];
if (!blob->pointer)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Pointer type exected for tag %d", blob->tag);
return FALSE;
}
/* FIXME validate length */
if (!validate_type_blob (typelib,
offset + G_STRUCT_OFFSET (ArrayTypeBlob, type),
0, FALSE, error))
return FALSE;
return TRUE;
}
static gboolean
validate_iface_type_blob (GTypelib *typelib,
guint32 offset,
guint32 signature_offset,
gboolean return_type,
GError **error)
{
InterfaceTypeBlob *blob;
InterfaceBlob *target;
blob = (InterfaceTypeBlob*)&typelib->data[offset];
target = (InterfaceBlob*) get_dir_entry_checked (typelib, blob->interface, error);
if (!target)
return FALSE;
if (target->blob_type == 0) /* non-local */
return TRUE;
return TRUE;
}
static gboolean
validate_param_type_blob (GTypelib *typelib,
guint32 offset,
guint32 signature_offset,
gboolean return_type,
gint n_params,
GError **error)
{
ParamTypeBlob *blob;
gint i;
blob = (ParamTypeBlob*)&typelib->data[offset];
if (!blob->pointer)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Pointer type exected for tag %d", blob->tag);
return FALSE;
}
if (blob->n_types != n_params)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Parameter type number mismatch");
return FALSE;
}
for (i = 0; i < n_params; i++)
{
if (!validate_type_blob (typelib,
offset + sizeof (ParamTypeBlob) +
i * sizeof (SimpleTypeBlob),
0, FALSE, error))
return FALSE;
}
return TRUE;
}
static gboolean
validate_error_type_blob (GTypelib *typelib,
guint32 offset,
guint32 signature_offset,
gboolean return_type,
GError **error)
{
ErrorTypeBlob *blob;
Header *header;
gint i;
DirEntry *entry;
blob = (ErrorTypeBlob*)&typelib->data[offset];
header = (Header *)typelib->data;
if (!blob->pointer)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Pointer type exected for tag %d", blob->tag);
return FALSE;
}
for (i = 0; i < blob->n_domains; i++)
{
if (blob->domains[i] == 0 || blob->domains[i] > header->n_entries)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid directory index %d", blob->domains[i]);
return FALSE;
}
entry = g_typelib_get_dir_entry (typelib, blob->domains[i]);
if (entry->blob_type != BLOB_TYPE_ERROR_DOMAIN &&
(entry->local || entry->blob_type != BLOB_TYPE_INVALID))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type");
return FALSE;
}
}
return TRUE;
}
static gboolean
validate_type_blob (GTypelib *typelib,
guint32 offset,
guint32 signature_offset,
gboolean return_type,
GError **error)
{
SimpleTypeBlob *simple;
InterfaceTypeBlob *iface;
simple = (SimpleTypeBlob *)&typelib->data[offset];
if (simple->reserved == 0 &&
simple->reserved2 == 0)
{
if (simple->tag >= GI_TYPE_TAG_ARRAY)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong tag in simple type");
return FALSE;
}
if (simple->tag >= GI_TYPE_TAG_UTF8 &&
!simple->pointer)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Pointer type exected for tag %d", simple->tag);
return FALSE;
}
return TRUE;
}
iface = (InterfaceTypeBlob*)&typelib->data[simple->offset];
switch (iface->tag)
{
case GI_TYPE_TAG_ARRAY:
if (!validate_array_type_blob (typelib, simple->offset,
signature_offset, return_type, error))
return FALSE;
break;
case GI_TYPE_TAG_INTERFACE:
if (!validate_iface_type_blob (typelib, simple->offset,
signature_offset, return_type, error))
return FALSE;
break;
case GI_TYPE_TAG_GLIST:
case GI_TYPE_TAG_GSLIST:
if (!validate_param_type_blob (typelib, simple->offset,
signature_offset, return_type, 1, error))
return FALSE;
break;
case GI_TYPE_TAG_GHASH:
if (!validate_param_type_blob (typelib, simple->offset,
signature_offset, return_type, 2, error))
return FALSE;
break;
case GI_TYPE_TAG_ERROR:
if (!validate_error_type_blob (typelib, simple->offset,
signature_offset, return_type, error))
return FALSE;
break;
default:
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong tag in complex type");
return FALSE;
}
return TRUE;
}
static gboolean
validate_arg_blob (GTypelib *typelib,
guint32 offset,
guint32 signature_offset,
GError **error)
{
ArgBlob *blob;
if (typelib->len < offset + sizeof (ArgBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (ArgBlob*) &typelib->data[offset];
if (!validate_name (typelib, "argument", typelib->data, blob->name, error))
return FALSE;
if (!validate_type_blob (typelib,
offset + G_STRUCT_OFFSET (ArgBlob, arg_type),
signature_offset, FALSE, error))
return FALSE;
return TRUE;
}
static SimpleTypeBlob *
return_type_from_signature (GTypelib *typelib,
guint32 offset,
GError **error)
{
SignatureBlob *blob;
if (typelib->len < offset + sizeof (SignatureBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return NULL;
}
blob = (SignatureBlob*) &typelib->data[offset];
if (blob->return_type.offset == 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"No return type found in signature");
return NULL;
}
return (SimpleTypeBlob *)&typelib->data[offset + G_STRUCT_OFFSET (SignatureBlob, return_type)];
}
static gboolean
validate_signature_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
SignatureBlob *blob;
gint i;
if (typelib->len < offset + sizeof (SignatureBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (SignatureBlob*) &typelib->data[offset];
if (blob->return_type.offset != 0)
{
if (!validate_type_blob (typelib,
offset + G_STRUCT_OFFSET (SignatureBlob, return_type),
offset, TRUE, error))
return FALSE;
}
for (i = 0; i < blob->n_arguments; i++)
{
if (!validate_arg_blob (typelib,
offset + sizeof (SignatureBlob) +
i * sizeof (ArgBlob),
offset,
error))
return FALSE;
}
/* FIXME check constraints on return_value */
/* FIXME check array-length pairs */
return TRUE;
}
static gboolean
validate_function_blob (ValidateContext *ctx,
guint32 offset,
guint16 container_type,
GError **error)
{
GTypelib *typelib = ctx->typelib;
FunctionBlob *blob;
SignatureBlob *sigblob;
gboolean is_method;
if (typelib->len < offset + sizeof (FunctionBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (FunctionBlob*) &typelib->data[offset];
if (blob->blob_type != BLOB_TYPE_FUNCTION)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type");
return FALSE;
}
if (!validate_name (typelib, "function", typelib->data, blob->name, error))
return FALSE;
push_context (ctx, get_string_nofail (typelib, blob->name));
if (!validate_name (typelib, "function symbol", typelib->data, blob->symbol, error))
return FALSE;
switch (container_type)
{
case BLOB_TYPE_BOXED:
case BLOB_TYPE_OBJECT:
case BLOB_TYPE_INTERFACE:
is_method = !(blob->constructor || blob->setter || blob->getter || blob->wraps_vfunc);
break;
default:
is_method = FALSE;
}
if (blob->constructor)
{
switch (container_type)
{
case BLOB_TYPE_BOXED:
case BLOB_TYPE_OBJECT:
case BLOB_TYPE_INTERFACE:
break;
default:
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Constructor not allowed");
return FALSE;
}
}
if (blob->setter || blob->getter || blob->wraps_vfunc)
{
switch (container_type)
{
case BLOB_TYPE_OBJECT:
case BLOB_TYPE_INTERFACE:
break;
default:
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Setter, getter or wrapper not allowed");
return FALSE;
}
}
if (blob->index)
{
if (!(blob->setter || blob->getter || blob->wraps_vfunc))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Must be setter, getter or wrapper");
return FALSE;
}
}
/* FIXME: validate index range */
if (!validate_signature_blob (typelib, blob->signature, error))
return FALSE;
sigblob = (SignatureBlob*) &typelib->data[blob->signature];
if (blob->constructor)
{
SimpleTypeBlob *simple = return_type_from_signature (typelib,
blob->signature,
error);
InterfaceTypeBlob *iface_type;
InterfaceBlob *iface;
if (!simple)
return FALSE;
iface_type = get_type_blob (typelib, simple, error);
if (!iface_type)
return FALSE;
if (!(iface_type->tag == GI_TYPE_TAG_INTERFACE))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"Invalid return type %d for constructor",
iface_type->tag);
return FALSE;
}
}
else if (is_method)
{
guint32 this_offset;
guint32 this_type_offset;
ArgBlob *this;
SimpleTypeBlob *thistype;
InterfaceTypeBlob *thistype_iface;
if (sigblob->n_arguments == 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"Invalid 0-argument method");
}
this_offset = blob->signature + sizeof (SignatureBlob);
this = (ArgBlob*) &typelib->data[this_offset];
this_type_offset = this_offset + G_STRUCT_OFFSET (ArgBlob, arg_type);
thistype = (SimpleTypeBlob *)&typelib->data[this_type_offset];
if (thistype->reserved == 0 &&
thistype->reserved2 == 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Non-reference type tag %d found for \"this\" argument",
thistype->tag);
return FALSE;
}
thistype_iface = (InterfaceTypeBlob*)&typelib->data[thistype->offset];
switch (thistype_iface->tag)
{
case GI_TYPE_TAG_INTERFACE:
break;
default:
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid type tag %d found for \"this\" argument",
thistype_iface->tag);
return FALSE;
}
}
pop_context (ctx);
return TRUE;
}
static gboolean
validate_callback_blob (ValidateContext *ctx,
guint32 offset,
GError **error)
{
GTypelib *typelib = ctx->typelib;
CallbackBlob *blob;
if (typelib->len < offset + sizeof (CallbackBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (CallbackBlob*) &typelib->data[offset];
if (blob->blob_type != BLOB_TYPE_CALLBACK)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type");
return FALSE;
}
if (!validate_name (typelib, "callback", typelib->data, blob->name, error))
return FALSE;
push_context (ctx, get_string_nofail (typelib, blob->name));
if (!validate_signature_blob (typelib, blob->signature, error))
return FALSE;
pop_context (ctx);
return TRUE;
}
static gboolean
validate_constant_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
gint value_size[] = {
0, 4, 1, 1, 2, 2, 4, 4, 8, 8,
sizeof (gint), sizeof (guint),
sizeof (glong), sizeof (gulong),
sizeof (gssize), sizeof (gsize),
sizeof (gfloat), sizeof (gdouble),
0, 0
};
ConstantBlob *blob;
SimpleTypeBlob *type;
if (typelib->len < offset + sizeof (ConstantBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (ConstantBlob*) &typelib->data[offset];
if (blob->blob_type != BLOB_TYPE_CONSTANT)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type");
return FALSE;
}
if (!validate_name (typelib, "constant", typelib->data, blob->name, error))
return FALSE;
if (!validate_type_blob (typelib, offset + G_STRUCT_OFFSET (ConstantBlob, type),
0, FALSE, error))
return FALSE;
if (!is_aligned (blob->offset))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Misaligned constant value");
return FALSE;
}
type = (SimpleTypeBlob *)&typelib->data[offset + G_STRUCT_OFFSET (ConstantBlob, type)];
if (type->reserved == 0 && type->reserved2 == 0)
{
if (type->tag == 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Constant value type void");
return FALSE;
}
if (value_size[type->tag] != 0 &&
blob->size != value_size[type->tag])
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Constant value size mismatch");
return FALSE;
}
/* FIXME check string values */
}
return TRUE;
}
static gboolean
validate_value_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
ValueBlob *blob;
if (typelib->len < offset + sizeof (ValueBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (ValueBlob*) &typelib->data[offset];
if (!validate_name (typelib, "value", typelib->data, blob->name, error))
return FALSE;
return TRUE;
}
static gboolean
validate_field_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
FieldBlob *blob;
if (typelib->len < offset + sizeof (FieldBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (FieldBlob*) &typelib->data[offset];
if (!validate_name (typelib, "field", typelib->data, blob->name, error))
return FALSE;
if (!validate_type_blob (typelib,
offset + G_STRUCT_OFFSET (FieldBlob, type),
0, FALSE, error))
return FALSE;
return TRUE;
}
static gboolean
validate_property_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
PropertyBlob *blob;
if (typelib->len < offset + sizeof (PropertyBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (PropertyBlob*) &typelib->data[offset];
if (!validate_name (typelib, "property", typelib->data, blob->name, error))
return FALSE;
if (!validate_type_blob (typelib,
offset + G_STRUCT_OFFSET (PropertyBlob, type),
0, FALSE, error))
return FALSE;
return TRUE;
}
static gboolean
validate_signal_blob (GTypelib *typelib,
guint32 offset,
guint32 container_offset,
GError **error)
{
SignalBlob *blob;
gint n_signals;
if (typelib->len < offset + sizeof (SignalBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (SignalBlob*) &typelib->data[offset];
if (!validate_name (typelib, "signal", typelib->data, blob->name, error))
return FALSE;
if ((blob->run_first != 0) +
(blob->run_last != 0) +
(blob->run_cleanup != 0) != 1)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid signal run flags");
return FALSE;
}
if (blob->has_class_closure)
{
if (((CommonBlob*)&typelib->data[container_offset])->blob_type == BLOB_TYPE_OBJECT)
{
ObjectBlob *object;
object = (ObjectBlob*)&typelib->data[container_offset];
n_signals = object->n_signals;
}
else
{
InterfaceBlob *iface;
iface = (InterfaceBlob*)&typelib->data[container_offset];
n_signals = iface->n_signals;
}
if (blob->class_closure >= n_signals)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid class closure index");
return FALSE;
}
}
if (!validate_signature_blob (typelib, blob->signature, error))
return FALSE;
return TRUE;
}
static gboolean
validate_vfunc_blob (GTypelib *typelib,
guint32 offset,
guint32 container_offset,
GError **error)
{
VFuncBlob *blob;
gint n_vfuncs;
if (typelib->len < offset + sizeof (VFuncBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (VFuncBlob*) &typelib->data[offset];
if (!validate_name (typelib, "vfunc", typelib->data, blob->name, error))
return FALSE;
if (blob->class_closure)
{
if (((CommonBlob*)&typelib->data[container_offset])->blob_type == BLOB_TYPE_OBJECT)
{
ObjectBlob *object;
object = (ObjectBlob*)&typelib->data[container_offset];
n_vfuncs = object->n_vfuncs;
}
else
{
InterfaceBlob *iface;
iface = (InterfaceBlob*)&typelib->data[container_offset];
n_vfuncs = iface->n_vfuncs;
}
if (blob->class_closure >= n_vfuncs)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid class closure index");
return FALSE;
}
}
if (!validate_signature_blob (typelib, blob->signature, error))
return FALSE;
return TRUE;
}
static gboolean
validate_struct_blob (ValidateContext *ctx,
guint32 offset,
guint16 blob_type,
GError **error)
{
GTypelib *typelib = ctx->typelib;
StructBlob *blob;
gint i;
if (typelib->len < offset + sizeof (StructBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (StructBlob*) &typelib->data[offset];
if (blob->blob_type != blob_type)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type");
return FALSE;
}
if (!validate_name (typelib, "struct", typelib->data, blob->name, error))
return FALSE;
push_context (ctx, get_string_nofail (typelib, blob->name));
if (!blob->unregistered)
{
if (!validate_name (typelib, "boxed", typelib->data, blob->gtype_name, error))
return FALSE;
if (!validate_name (typelib, "boxed", typelib->data, blob->gtype_init, error))
return FALSE;
}
else
{
if (blob->gtype_name || blob->gtype_init)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Gtype data in struct");
return FALSE;
}
}
if (typelib->len < offset + sizeof (StructBlob) +
blob->n_fields * sizeof (FieldBlob) +
blob->n_methods * sizeof (FunctionBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
for (i = 0; i < blob->n_fields; i++)
{
if (!validate_field_blob (typelib,
offset + sizeof (StructBlob) +
i * sizeof (FieldBlob),
error))
return FALSE;
}
for (i = 0; i < blob->n_methods; i++)
{
if (!validate_function_blob (ctx,
offset + sizeof (StructBlob) +
blob->n_fields * sizeof (FieldBlob) +
i * sizeof (FunctionBlob),
blob_type,
error))
return FALSE;
}
pop_context (ctx);
return TRUE;
}
static gboolean
validate_enum_blob (ValidateContext *ctx,
guint32 offset,
guint16 blob_type,
GError **error)
{
GTypelib *typelib = ctx->typelib;
EnumBlob *blob;
ValueBlob *v1, *v2;
gint i, j;
if (typelib->len < offset + sizeof (EnumBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (EnumBlob*) &typelib->data[offset];
if (blob->blob_type != blob_type)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type");
return FALSE;
}
if (!blob->unregistered)
{
if (!validate_name (typelib, "enum", typelib->data, blob->gtype_name, error))
return FALSE;
if (!validate_name (typelib, "enum", typelib->data, blob->gtype_init, error))
return FALSE;
}
else
{
if (blob->gtype_name || blob->gtype_init)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Gtype data in unregistered enum");
return FALSE;
}
}
if (!validate_name (typelib, "enum", typelib->data, blob->name, error))
return FALSE;
if (typelib->len < offset + sizeof (EnumBlob) +
blob->n_values * sizeof (ValueBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
push_context (ctx, get_string_nofail (typelib, blob->name));
for (i = 0; i < blob->n_values; i++)
{
if (!validate_value_blob (typelib,
offset + sizeof (EnumBlob) +
i * sizeof (ValueBlob),
error))
return FALSE;
#if 0
v1 = (ValueBlob *)&typelib->data[offset + sizeof (EnumBlob) +
i * sizeof (ValueBlob)];
for (j = 0; j < i; j++)
{
v2 = (ValueBlob *)&typelib->data[offset + sizeof (EnumBlob) +
j * sizeof (ValueBlob)];
if (v1->value == v2->value)
{
/* FIXME should this be an error ? */
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Duplicate enum value");
return FALSE;
}
}
#endif
}
pop_context (ctx);
return TRUE;
}
static gboolean
validate_object_blob (ValidateContext *ctx,
guint32 offset,
GError **error)
{
GTypelib *typelib = ctx->typelib;
Header *header;
ObjectBlob *blob;
gint i;
guint32 offset2;
header = (Header *)typelib->data;
if (typelib->len < offset + sizeof (ObjectBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (ObjectBlob*) &typelib->data[offset];
if (blob->blob_type != BLOB_TYPE_OBJECT)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type");
return FALSE;
}
if (!validate_name (typelib, "object", typelib->data, blob->gtype_name, error))
return FALSE;
if (!validate_name (typelib, "object", typelib->data, blob->gtype_init, error))
return FALSE;
if (!validate_name (typelib, "object", typelib->data, blob->name, error))
return FALSE;
if (blob->parent > header->n_entries)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid parent index");
return FALSE;
}
if (blob->parent != 0)
{
DirEntry *entry;
entry = g_typelib_get_dir_entry (typelib, blob->parent);
if (entry->blob_type != BLOB_TYPE_OBJECT &&
(entry->local || entry->blob_type != 0))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Parent not object");
return FALSE;
}
}
if (typelib->len < offset + sizeof (ObjectBlob) +
(blob->n_interfaces + blob->n_interfaces % 2) * 2 +
blob->n_fields * sizeof (FieldBlob) +
blob->n_properties * sizeof (PropertyBlob) +
blob->n_methods * sizeof (FunctionBlob) +
blob->n_signals * sizeof (SignalBlob) +
blob->n_vfuncs * sizeof (VFuncBlob) +
blob->n_constants * sizeof (ConstantBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
offset2 = offset + sizeof (ObjectBlob);
for (i = 0; i < blob->n_interfaces; i++, offset2 += 2)
{
guint16 iface;
DirEntry *entry;
iface = *(guint16*)&typelib->data[offset2];
if (iface == 0 || iface > header->n_entries)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid interface index");
return FALSE;
}
entry = g_typelib_get_dir_entry (typelib, iface);
if (entry->blob_type != BLOB_TYPE_INTERFACE &&
(entry->local || entry->blob_type != 0))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Not an interface");
return FALSE;
}
}
offset2 += 2 * (blob->n_interfaces %2);
push_context (ctx, get_string_nofail (typelib, blob->name));
for (i = 0; i < blob->n_fields; i++, offset2 += sizeof (FieldBlob))
{
if (!validate_field_blob (typelib, offset2, error))
return FALSE;
}
for (i = 0; i < blob->n_properties; i++, offset2 += sizeof (PropertyBlob))
{
if (!validate_property_blob (typelib, offset2, error))
return FALSE;
}
for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob))
{
if (!validate_function_blob (ctx, offset2, BLOB_TYPE_OBJECT, error))
return FALSE;
}
for (i = 0; i < blob->n_signals; i++, offset2 += sizeof (SignalBlob))
{
if (!validate_signal_blob (typelib, offset2, offset, error))
return FALSE;
}
for (i = 0; i < blob->n_vfuncs; i++, offset2 += sizeof (VFuncBlob))
{
if (!validate_vfunc_blob (typelib, offset2, offset, error))
return FALSE;
}
for (i = 0; i < blob->n_constants; i++, offset2 += sizeof (ConstantBlob))
{
if (!validate_constant_blob (typelib, offset2, error))
return FALSE;
}
pop_context (ctx);
return TRUE;
}
static gboolean
validate_interface_blob (ValidateContext *ctx,
guint32 offset,
GError **error)
{
GTypelib *typelib = ctx->typelib;
Header *header;
InterfaceBlob *blob;
gint i;
guint32 offset2;
header = (Header *)typelib->data;
if (typelib->len < offset + sizeof (InterfaceBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
blob = (InterfaceBlob*) &typelib->data[offset];
if (blob->blob_type != BLOB_TYPE_INTERFACE)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Wrong blob type; expected interface, got %d", blob->blob_type);
return FALSE;
}
if (!validate_name (typelib, "interface", typelib->data, blob->gtype_name, error))
return FALSE;
if (!validate_name (typelib, "interface", typelib->data, blob->gtype_init, error))
return FALSE;
if (!validate_name (typelib, "interface", typelib->data, blob->name, error))
return FALSE;
if (typelib->len < offset + sizeof (InterfaceBlob) +
(blob->n_prerequisites + blob->n_prerequisites % 2) * 2 +
blob->n_properties * sizeof (PropertyBlob) +
blob->n_methods * sizeof (FunctionBlob) +
blob->n_signals * sizeof (SignalBlob) +
blob->n_vfuncs * sizeof (VFuncBlob) +
blob->n_constants * sizeof (ConstantBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
offset2 = offset + sizeof (InterfaceBlob);
for (i = 0; i < blob->n_prerequisites; i++, offset2 += 2)
{
DirEntry *entry;
guint16 req;
req = *(guint16*)&typelib->data[offset2];
if (req == 0 || req > header->n_entries)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Invalid prerequisite index");
return FALSE;
}
entry = g_typelib_get_dir_entry (typelib, req);
if (entry->blob_type != BLOB_TYPE_INTERFACE &&
entry->blob_type != BLOB_TYPE_OBJECT &&
(entry->local || entry->blob_type != 0))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_BLOB,
"Not an interface or object");
return FALSE;
}
}
offset2 += 2 * (blob->n_prerequisites % 2);
push_context (ctx, get_string_nofail (typelib, blob->name));
for (i = 0; i < blob->n_properties; i++, offset2 += sizeof (PropertyBlob))
{
if (!validate_property_blob (typelib, offset2, error))
return FALSE;
}
for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob))
{
if (!validate_function_blob (ctx, offset2, BLOB_TYPE_INTERFACE, error))
return FALSE;
}
for (i = 0; i < blob->n_signals; i++, offset2 += sizeof (SignalBlob))
{
if (!validate_signal_blob (typelib, offset2, offset, error))
return FALSE;
}
for (i = 0; i < blob->n_vfuncs; i++, offset2 += sizeof (VFuncBlob))
{
if (!validate_vfunc_blob (typelib, offset2, offset, error))
return FALSE;
}
for (i = 0; i < blob->n_constants; i++, offset2 += sizeof (ConstantBlob))
{
if (!validate_constant_blob (typelib, offset2, error))
return FALSE;
}
pop_context (ctx);
return TRUE;
}
static gboolean
validate_errordomain_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
return TRUE;
}
static gboolean
validate_union_blob (GTypelib *typelib,
guint32 offset,
GError **error)
{
return TRUE;
}
static gboolean
validate_blob (ValidateContext *ctx,
guint32 offset,
GError **error)
{
GTypelib *typelib = ctx->typelib;
CommonBlob *common;
if (typelib->len < offset + sizeof (CommonBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
common = (CommonBlob*)&typelib->data[offset];
switch (common->blob_type)
{
case BLOB_TYPE_FUNCTION:
if (!validate_function_blob (ctx, offset, 0, error))
return FALSE;
break;
case BLOB_TYPE_CALLBACK:
if (!validate_callback_blob (ctx, offset, error))
return FALSE;
break;
case BLOB_TYPE_STRUCT:
case BLOB_TYPE_BOXED:
if (!validate_struct_blob (ctx, offset, common->blob_type, error))
return FALSE;
break;
case BLOB_TYPE_ENUM:
case BLOB_TYPE_FLAGS:
if (!validate_enum_blob (ctx, offset, common->blob_type, error))
return FALSE;
break;
case BLOB_TYPE_OBJECT:
if (!validate_object_blob (ctx, offset, error))
return FALSE;
break;
case BLOB_TYPE_INTERFACE:
if (!validate_interface_blob (ctx, offset, error))
return FALSE;
break;
case BLOB_TYPE_CONSTANT:
if (!validate_constant_blob (typelib, offset, error))
return FALSE;
break;
case BLOB_TYPE_ERROR_DOMAIN:
if (!validate_errordomain_blob (typelib, offset, error))
return FALSE;
break;
case BLOB_TYPE_UNION:
if (!validate_union_blob (typelib, offset, error))
return FALSE;
break;
default:
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_ENTRY,
"Invalid blob type");
return FALSE;
}
return TRUE;
}
static gboolean
validate_directory (ValidateContext *ctx,
GError **error)
{
GTypelib *typelib = ctx->typelib;
Header *header = (Header *)typelib->data;
DirEntry *entry;
gint i;
if (typelib->len < header->directory + header->n_entries * sizeof (DirEntry))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
for (i = 0; i < header->n_entries; i++)
{
entry = g_typelib_get_dir_entry (typelib, i + 1);
if (!validate_name (typelib, "entry", typelib->data, entry->name, error))
return FALSE;
if ((entry->local && entry->blob_type == BLOB_TYPE_INVALID) ||
entry->blob_type > BLOB_TYPE_UNION)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_DIRECTORY,
"Invalid entry type");
return FALSE;
}
if (i < header->n_local_entries)
{
if (!entry->local)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_DIRECTORY,
"Too few local directory entries");
return FALSE;
}
if (!is_aligned (entry->offset))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_DIRECTORY,
"Misaligned entry");
return FALSE;
}
if (!validate_blob (ctx, entry->offset, error))
return FALSE;
}
else
{
if (entry->local)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_DIRECTORY,
"Too many local directory entries");
return FALSE;
}
if (!validate_name (typelib, "namespace", typelib->data, entry->offset, error))
return FALSE;
}
}
return TRUE;
}
static gboolean
validate_annotations (ValidateContext *ctx,
GError **error)
{
GTypelib *typelib = ctx->typelib;
Header *header = (Header *)typelib->data;
if (header->size < header->annotations + header->n_annotations * sizeof (AnnotationBlob))
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID,
"The buffer is too short");
return FALSE;
}
return TRUE;
}
static void
prefix_with_context (GError **error,
const char *section,
ValidateContext *ctx)
{
GString *str = g_string_new (NULL);
GSList *link;
char *buf;
link = ctx->context_stack;
if (!link)
{
g_prefix_error (error, "In %s:", section);
return;
}
for (; link; link = link->next)
{
g_string_append (str, link->data);
if (link->next)
g_string_append_c (str, '/');
}
g_string_append_c (str, ')');
buf = g_string_free (str, FALSE);
g_prefix_error (error, "In %s (Context: %s): ", section, buf);
g_free (buf);
}
gboolean
g_typelib_validate (GTypelib *typelib,
GError **error)
{
ValidateContext ctx;
ctx.typelib = typelib;
ctx.context_stack = NULL;
if (!validate_header (&ctx, error))
{
prefix_with_context (error, "In header", &ctx);
return FALSE;
}
if (!validate_directory (&ctx, error))
{
prefix_with_context (error, "directory", &ctx);
return FALSE;
}
if (!validate_annotations (&ctx, error))
{
prefix_with_context (error, "annotations", &ctx);
return FALSE;
}
return TRUE;
}
GQuark
g_typelib_error_quark (void)
{
static GQuark quark = 0;
if (quark == 0)
quark = g_quark_from_static_string ("g-typelib-error-quark");
return quark;
}
static const char*
find_some_symbol (GTypelib *typelib)
{
Header *header = (Header *) typelib->data;
gint i;
for (i = 0; i < header->n_entries; i++)
{
DirEntry *entry;
entry = g_typelib_get_dir_entry (typelib, i + 1);
switch (entry->blob_type)
{
case BLOB_TYPE_FUNCTION:
{
FunctionBlob *blob = (FunctionBlob *) &typelib->data[entry->offset];
if (blob->symbol)
return g_typelib_get_string (typelib, blob->symbol);
}
break;
case BLOB_TYPE_OBJECT:
{
RegisteredTypeBlob *blob = (RegisteredTypeBlob *) &typelib->data[entry->offset];
if (blob->gtype_init)
return g_typelib_get_string (typelib, blob->gtype_init);
}
break;
default:
break;
}
}
return NULL;
}
static inline void
_g_typelib_init (GTypelib *typelib)
{
Header *header;
header = (Header *) typelib->data;
if (header->shared_library)
{
const gchar *shlib;
shlib = g_typelib_get_string (typelib, header->shared_library);
/* note that NULL shlib means to open the main app, which is allowed */
/* If we do have a shared lib, first be sure the main app isn't already linked to it */
if (shlib != NULL)
{
const char *symbol_in_module;
symbol_in_module = find_some_symbol (typelib);
if (symbol_in_module != NULL)
{
typelib->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
if (typelib->module == NULL)
{
g_warning ("Could not open main app as GModule: %s",
g_module_error ());
}
else
{
void *sym;
if (!g_module_symbol (typelib->module, symbol_in_module, &sym))
{
/* we will try opening the shlib, symbol is not in app already */
g_module_close (typelib->module);
typelib->module = NULL;
}
}
}
else
{
g_warning ("Could not find any symbols in typelib");
}
}
if (typelib->module == NULL && shlib != NULL)
{
GString *shlib_full;
/* Glade's autoconnect feature and OpenGL's extension mechanism
* as used by Clutter rely on dlopen(NULL) to work as a means of
* accessing the app's symbols. This keeps us from using
* G_MODULE_BIND_LOCAL. BIND_LOCAL may have other issues as well;
* in general libraries are not expecting multiple copies of
* themselves and are not expecting to be unloaded. So we just
* load modules globally for now.
*/
typelib->module = g_module_open (shlib, G_MODULE_BIND_LAZY);
if (typelib->module == NULL)
{
shlib_full = g_string_new (shlib);
/* Prefix with "lib", try both .la and .so */
if (!g_str_has_prefix (shlib_full->str, "lib"))
g_string_prepend (shlib_full, "lib");
g_string_append (shlib_full, ".la");
typelib->module = g_module_open (shlib_full->str, G_MODULE_BIND_LAZY);
if (typelib->module == NULL)
g_string_overwrite (shlib_full, strlen (shlib_full->str)-2, SHLIB_SUFFIX);
typelib->module = g_module_open (shlib_full->str, G_MODULE_BIND_LAZY);
g_string_free (shlib_full, TRUE);
}
if (typelib->module == NULL)
g_warning ("Failed to load shared library '%s' referenced by the typelib: %s",
shlib, g_module_error ());
}
}
}
/**
* g_typelib_new_from_memory:
* @memory: address of memory chunk containing the typelib
* @len: length of memory chunk containing the typelib
*
* Creates a new #GTypelib from a memory location. The memory block
* pointed to by @typelib will be automatically g_free()d when the
* repository is destroyed.
*
* Return value: the new #GTypelib
**/
GTypelib *
g_typelib_new_from_memory (guchar *memory, gsize len)
{
GTypelib *meta;
meta = g_new0 (GTypelib, 1);
meta->data = memory;
meta->len = len;
meta->owns_memory = TRUE;
_g_typelib_init (meta);
return meta;
}
/**
* g_typelib_new_from_const_memory:
* @memory: address of memory chunk containing the typelib
* @len: length of memory chunk containing the typelib
*
* Creates a new #GTypelib from a memory location.
*
* Return value: the new #GTypelib
**/
GTypelib *
g_typelib_new_from_const_memory (const guchar *memory, gsize len)
{
GTypelib *meta;
meta = g_new0 (GTypelib, 1);
meta->data = (guchar *) memory;
meta->len = len;
meta->owns_memory = FALSE;
_g_typelib_init (meta);
return meta;
}
/**
* g_typelib_new_from_mapped_file:
* @mfile: a #GMappedFile, that will be free'd when the repository is destroyed
*
* Creates a new #GTypelib from a #GMappedFile.
*
* Return value: the new #GTypelib
**/
GTypelib *
g_typelib_new_from_mapped_file (GMappedFile *mfile)
{
GTypelib *meta;
meta = g_new0 (GTypelib, 1);
meta->mfile = mfile;
meta->owns_memory = FALSE;
meta->data = (guchar *) g_mapped_file_get_contents (mfile);
meta->len = g_mapped_file_get_length (mfile);
_g_typelib_init (meta);
return meta;
}
/**
* g_typelib_free:
* @typelib: a #GTypelib
*
* Free a #GTypelib.
**/
void
g_typelib_free (GTypelib *typelib)
{
if (typelib->mfile)
g_mapped_file_free (typelib->mfile);
else
if (typelib->owns_memory)
g_free (typelib->data);
if (typelib->module)
g_module_close (typelib->module);
g_free (typelib);
}
/**
* g_typelib_set_module:
* @typelib: a #GTypelib instance
* @module: a #GModule; takes ownership of this module
*
* Sets the target module for all symbols referenced by the typelib.
**/
void
g_typelib_set_module (GTypelib *typelib, GModule *module)
{
if (typelib->module)
g_module_close (typelib->module);
typelib->module = module;
}
const gchar *
g_typelib_get_namespace(GTypelib *typelib)
{
return g_typelib_get_string (typelib, ((Header *) typelib->data)->namespace);
}