/**
|
* \file
|
* Routine for saving an image to a file.
|
*
|
*
|
* Author:
|
* Paolo Molaro (lupus@ximian.com)
|
*
|
* Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
|
* Copyright 2004-2009 Novell, Inc (http://www.novell.com)
|
* Copyright 2011 Rodrigo Kumpera
|
* Copyright 2016 Microsoft
|
*
|
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
*/
|
|
#include <config.h>
|
#include <glib.h>
|
|
#include "mono/metadata/dynamic-image-internals.h"
|
#include "mono/metadata/dynamic-stream-internals.h"
|
#include "mono/metadata/mono-ptr-array.h"
|
#include "mono/metadata/object-internals.h"
|
#include "mono/metadata/sre-internals.h"
|
#include "mono/metadata/security-manager.h"
|
#include "mono/metadata/tabledefs.h"
|
#include "mono/metadata/tokentype.h"
|
#include "mono/metadata/w32file.h"
|
#include "mono/metadata/w32error.h"
|
|
#include "mono/utils/checked-build.h"
|
#include "mono/utils/mono-digest.h"
|
#include "mono/utils/mono-error-internals.h"
|
#include "mono/utils/w32api.h"
|
|
#define TEXT_OFFSET 512
|
#define CLI_H_SIZE 136
|
#define FILE_ALIGN 512
|
#define VIRT_ALIGN 8192
|
#define START_TEXT_RVA 0x00002000
|
|
static void mono_image_get_generic_param_info (MonoReflectionGenericParam *gparam, guint32 owner, MonoDynamicImage *assembly);
|
|
static void
|
alloc_table (MonoDynamicTable *table, guint nrows)
|
{
|
mono_dynimage_alloc_table (table, nrows);
|
}
|
|
static guint32
|
string_heap_insert (MonoDynamicStream *sh, const char *str)
|
{
|
return mono_dynstream_insert_string (sh, str);
|
}
|
|
static guint32
|
string_heap_insert_mstring (MonoDynamicStream *sh, MonoString *str, MonoError *error)
|
{
|
return mono_dynstream_insert_mstring (sh, str, error);
|
}
|
|
static guint32
|
mono_image_add_stream_data (MonoDynamicStream *stream, const char *data, guint32 len)
|
{
|
return mono_dynstream_add_data (stream, data, len);
|
}
|
|
static guint32
|
mono_image_add_stream_zero (MonoDynamicStream *stream, guint32 len)
|
{
|
return mono_dynstream_add_zero (stream, len);
|
}
|
|
static void
|
stream_data_align (MonoDynamicStream *stream)
|
{
|
mono_dynstream_data_align (stream);
|
}
|
|
static guint32
|
mono_image_typedef_or_ref (MonoDynamicImage *assembly, MonoType *type)
|
{
|
return mono_dynimage_encode_typedef_or_ref_full (assembly, type, TRUE);
|
}
|
|
static guint32
|
find_index_in_table (MonoDynamicImage *assembly, int table_idx, int col, guint32 token)
|
{
|
MONO_REQ_GC_NEUTRAL_MODE;
|
|
int i;
|
MonoDynamicTable *table;
|
guint32 *values;
|
|
table = &assembly->tables [table_idx];
|
|
g_assert (col < table->columns);
|
|
values = table->values + table->columns;
|
for (i = 1; i <= table->rows; ++i) {
|
if (values [col] == token)
|
return i;
|
values += table->columns;
|
}
|
return 0;
|
}
|
|
/*
|
* Copy len * nelem bytes from val to dest, swapping bytes to LE if necessary.
|
* dest may be misaligned.
|
*/
|
static void
|
swap_with_size (char *dest, const char* val, int len, int nelem) {
|
MONO_REQ_GC_NEUTRAL_MODE;
|
#if G_BYTE_ORDER != G_LITTLE_ENDIAN
|
int elem;
|
|
for (elem = 0; elem < nelem; ++elem) {
|
switch (len) {
|
case 1:
|
*dest = *val;
|
break;
|
case 2:
|
dest [0] = val [1];
|
dest [1] = val [0];
|
break;
|
case 4:
|
dest [0] = val [3];
|
dest [1] = val [2];
|
dest [2] = val [1];
|
dest [3] = val [0];
|
break;
|
case 8:
|
dest [0] = val [7];
|
dest [1] = val [6];
|
dest [2] = val [5];
|
dest [3] = val [4];
|
dest [4] = val [3];
|
dest [5] = val [2];
|
dest [6] = val [1];
|
dest [7] = val [0];
|
break;
|
default:
|
g_assert_not_reached ();
|
}
|
dest += len;
|
val += len;
|
}
|
#else
|
memcpy (dest, val, len * nelem);
|
#endif
|
}
|
|
static guint32
|
add_mono_string_to_blob_cached (MonoDynamicImage *assembly, MonoString *str)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
char blob_size [64];
|
char *b = blob_size;
|
guint32 idx = 0, len;
|
|
len = str->length * 2;
|
mono_metadata_encode_value (len, b, &b);
|
#if G_BYTE_ORDER != G_LITTLE_ENDIAN
|
{
|
char *swapped = g_malloc (2 * mono_string_length (str));
|
const char *p = (const char*)mono_string_chars (str);
|
|
swap_with_size (swapped, p, 2, mono_string_length (str));
|
idx = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, swapped, len);
|
g_free (swapped);
|
}
|
#else
|
idx = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, b-blob_size, (char*)mono_string_chars (str), len);
|
#endif
|
return idx;
|
}
|
|
static guint32
|
image_create_token_raw (MonoDynamicImage *assembly, MonoObject* obj_raw, gboolean create_methodspec, gboolean register_token, MonoError *error)
|
{
|
HANDLE_FUNCTION_ENTER (); /* FIXME callers of image_create_token_raw should use handles */
|
error_init (error);
|
MONO_HANDLE_DCL (MonoObject, obj);
|
guint32 result = mono_image_create_token (assembly, obj, create_methodspec, register_token, error);
|
HANDLE_FUNCTION_RETURN_VAL (result);
|
}
|
|
|
/*
|
* idx is the table index of the object
|
* type is one of MONO_CUSTOM_ATTR_*
|
*/
|
static gboolean
|
mono_image_add_cattrs (MonoDynamicImage *assembly, guint32 idx, guint32 type, MonoArray *cattrs, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
MonoDynamicTable *table;
|
MonoReflectionCustomAttr *cattr;
|
guint32 *values;
|
guint32 count, i, token;
|
char blob_size [6];
|
char *p = blob_size;
|
|
error_init (error);
|
|
/* it is legal to pass a NULL cattrs: we avoid to use the if in a lot of places */
|
if (!cattrs)
|
return TRUE;
|
count = mono_array_length (cattrs);
|
table = &assembly->tables [MONO_TABLE_CUSTOMATTRIBUTE];
|
table->rows += count;
|
alloc_table (table, table->rows);
|
values = table->values + table->next_idx * MONO_CUSTOM_ATTR_SIZE;
|
idx <<= MONO_CUSTOM_ATTR_BITS;
|
idx |= type;
|
for (i = 0; i < count; ++i) {
|
cattr = (MonoReflectionCustomAttr*)mono_array_get (cattrs, gpointer, i);
|
values [MONO_CUSTOM_ATTR_PARENT] = idx;
|
g_assert (cattr->ctor != NULL);
|
if (mono_is_sre_ctor_builder (mono_object_class (cattr->ctor))) {
|
MonoReflectionCtorBuilder *ctor = (MonoReflectionCtorBuilder*)cattr->ctor;
|
MonoMethod *method = ctor->mhandle;
|
if (method->klass->image == &assembly->image)
|
token = MONO_TOKEN_METHOD_DEF | ((MonoReflectionCtorBuilder*)cattr->ctor)->table_idx;
|
else
|
token = mono_image_get_methodref_token (assembly, method, FALSE);
|
} else {
|
token = image_create_token_raw (assembly, (MonoObject*)cattr->ctor, FALSE, FALSE, error); /* FIXME use handles */
|
if (!mono_error_ok (error)) goto fail;
|
}
|
type = mono_metadata_token_index (token);
|
type <<= MONO_CUSTOM_ATTR_TYPE_BITS;
|
switch (mono_metadata_token_table (token)) {
|
case MONO_TABLE_METHOD:
|
type |= MONO_CUSTOM_ATTR_TYPE_METHODDEF;
|
/*
|
* fixup_cattrs () needs to fix this up. We can't use image->tokens, since it contains the old token for the
|
* method, not the one returned by mono_image_create_token ().
|
*/
|
mono_g_hash_table_insert (assembly->remapped_tokens, GUINT_TO_POINTER (token), cattr->ctor);
|
break;
|
case MONO_TABLE_MEMBERREF:
|
type |= MONO_CUSTOM_ATTR_TYPE_MEMBERREF;
|
break;
|
default:
|
g_warning ("got wrong token in custom attr");
|
continue;
|
}
|
values [MONO_CUSTOM_ATTR_TYPE] = type;
|
p = blob_size;
|
mono_metadata_encode_value (mono_array_length (cattr->data), p, &p);
|
values [MONO_CUSTOM_ATTR_VALUE] = mono_dynamic_image_add_to_blob_cached (assembly, blob_size, p - blob_size,
|
mono_array_addr (cattr->data, char, 0), mono_array_length (cattr->data));
|
values += MONO_CUSTOM_ATTR_SIZE;
|
++table->next_idx;
|
}
|
|
return TRUE;
|
|
fail:
|
return FALSE;
|
}
|
|
static void
|
mono_image_add_decl_security (MonoDynamicImage *assembly, guint32 parent_token, MonoArray *permissions)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint32 count, i, idx;
|
MonoReflectionPermissionSet *perm;
|
|
if (!permissions)
|
return;
|
|
count = mono_array_length (permissions);
|
table = &assembly->tables [MONO_TABLE_DECLSECURITY];
|
table->rows += count;
|
alloc_table (table, table->rows);
|
|
for (i = 0; i < mono_array_length (permissions); ++i) {
|
perm = (MonoReflectionPermissionSet*)mono_array_addr (permissions, MonoReflectionPermissionSet, i);
|
|
values = table->values + table->next_idx * MONO_DECL_SECURITY_SIZE;
|
|
idx = mono_metadata_token_index (parent_token);
|
idx <<= MONO_HAS_DECL_SECURITY_BITS;
|
switch (mono_metadata_token_table (parent_token)) {
|
case MONO_TABLE_TYPEDEF:
|
idx |= MONO_HAS_DECL_SECURITY_TYPEDEF;
|
break;
|
case MONO_TABLE_METHOD:
|
idx |= MONO_HAS_DECL_SECURITY_METHODDEF;
|
break;
|
case MONO_TABLE_ASSEMBLY:
|
idx |= MONO_HAS_DECL_SECURITY_ASSEMBLY;
|
break;
|
default:
|
g_assert_not_reached ();
|
}
|
|
values [MONO_DECL_SECURITY_ACTION] = perm->action;
|
values [MONO_DECL_SECURITY_PARENT] = idx;
|
values [MONO_DECL_SECURITY_PERMISSIONSET] = add_mono_string_to_blob_cached (assembly, perm->pset);
|
|
++table->next_idx;
|
}
|
}
|
|
/**
|
* method_encode_code:
|
*
|
* @assembly the assembly
|
* @mb the managed MethodBuilder
|
* @error set on error
|
*
|
* Note that the return value is not sensible if @error is set.
|
*/
|
static guint32
|
method_encode_code (MonoDynamicImage *assembly, ReflectionMethodBuilder *mb, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
char flags = 0;
|
guint32 idx;
|
guint32 code_size;
|
gint32 max_stack, i;
|
gint32 num_locals = 0;
|
gint32 num_exception = 0;
|
gint maybe_small;
|
guint32 fat_flags;
|
char fat_header [12];
|
guint32 int_value;
|
guint16 short_value;
|
guint32 local_sig = 0;
|
guint32 header_size = 12;
|
MonoArray *code;
|
|
error_init (error);
|
|
if ((mb->attrs & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT)) ||
|
(mb->iattrs & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)))
|
return 0;
|
|
/*if (mb->name)
|
g_print ("Encode method %s\n", mono_string_to_utf8 (mb->name));*/
|
if (mb->ilgen) {
|
code = mb->ilgen->code;
|
code_size = mb->ilgen->code_len;
|
max_stack = mb->ilgen->max_stack;
|
num_locals = mb->ilgen->locals ? mono_array_length (mb->ilgen->locals) : 0;
|
if (mb->ilgen->ex_handlers)
|
num_exception = mono_reflection_method_count_clauses (mb->ilgen);
|
} else {
|
code = mb->code;
|
if (code == NULL){
|
MonoError inner_error;
|
char *name = mono_string_to_utf8_checked (mb->name, &inner_error);
|
if (!is_ok (&inner_error)) {
|
name = g_strdup ("");
|
mono_error_cleanup (&inner_error);
|
}
|
char *str = g_strdup_printf ("Method %s does not have any IL associated", name);
|
mono_error_set_argument (error, NULL, "a method does not have any IL associated");
|
g_free (str);
|
g_free (name);
|
return 0;
|
}
|
|
code_size = mono_array_length (code);
|
max_stack = 8; /* we probably need to run a verifier on the code... */
|
}
|
|
stream_data_align (&assembly->code);
|
|
/* check for exceptions, maxstack, locals */
|
maybe_small = (max_stack <= 8) && (!num_locals) && (!num_exception);
|
if (maybe_small) {
|
if (code_size < 64 && !(code_size & 1)) {
|
flags = (code_size << 2) | 0x2;
|
} else if (code_size < 32 && (code_size & 1)) {
|
flags = (code_size << 2) | 0x6; /* LAMESPEC: see metadata.c */
|
} else {
|
goto fat_header;
|
}
|
idx = mono_image_add_stream_data (&assembly->code, &flags, 1);
|
/* add to the fixup todo list */
|
if (mb->ilgen && mb->ilgen->num_token_fixups)
|
mono_g_hash_table_insert (assembly->token_fixups, mb->ilgen, GUINT_TO_POINTER (idx + 1));
|
mono_image_add_stream_data (&assembly->code, mono_array_addr (code, char, 0), code_size);
|
return assembly->text_rva + idx;
|
}
|
fat_header:
|
if (num_locals) {
|
local_sig = MONO_TOKEN_SIGNATURE | mono_dynimage_encode_locals (assembly, mb->ilgen, error);
|
return_val_if_nok (error, 0);
|
}
|
/*
|
* FIXME: need to set also the header size in fat_flags.
|
* (and more sects and init locals flags)
|
*/
|
fat_flags = 0x03;
|
if (num_exception)
|
fat_flags |= METHOD_HEADER_MORE_SECTS;
|
if (mb->init_locals)
|
fat_flags |= METHOD_HEADER_INIT_LOCALS;
|
fat_header [0] = fat_flags;
|
fat_header [1] = (header_size / 4 ) << 4;
|
short_value = GUINT16_TO_LE (max_stack);
|
memcpy (fat_header + 2, &short_value, 2);
|
int_value = GUINT32_TO_LE (code_size);
|
memcpy (fat_header + 4, &int_value, 4);
|
int_value = GUINT32_TO_LE (local_sig);
|
memcpy (fat_header + 8, &int_value, 4);
|
idx = mono_image_add_stream_data (&assembly->code, fat_header, 12);
|
/* add to the fixup todo list */
|
if (mb->ilgen && mb->ilgen->num_token_fixups)
|
mono_g_hash_table_insert (assembly->token_fixups, mb->ilgen, GUINT_TO_POINTER (idx + 12));
|
|
mono_image_add_stream_data (&assembly->code, mono_array_addr (code, char, 0), code_size);
|
if (num_exception) {
|
unsigned char sheader [4];
|
MonoILExceptionInfo * ex_info;
|
MonoILExceptionBlock * ex_block;
|
int j;
|
|
stream_data_align (&assembly->code);
|
/* always use fat format for now */
|
sheader [0] = METHOD_HEADER_SECTION_FAT_FORMAT | METHOD_HEADER_SECTION_EHTABLE;
|
num_exception *= 6 * sizeof (guint32);
|
num_exception += 4; /* include the size of the header */
|
sheader [1] = num_exception & 0xff;
|
sheader [2] = (num_exception >> 8) & 0xff;
|
sheader [3] = (num_exception >> 16) & 0xff;
|
mono_image_add_stream_data (&assembly->code, (char*)sheader, 4);
|
/* fat header, so we are already aligned */
|
/* reverse order */
|
for (i = mono_array_length (mb->ilgen->ex_handlers) - 1; i >= 0; --i) {
|
ex_info = (MonoILExceptionInfo *)mono_array_addr (mb->ilgen->ex_handlers, MonoILExceptionInfo, i);
|
if (ex_info->handlers) {
|
int finally_start = ex_info->start + ex_info->len;
|
for (j = 0; j < mono_array_length (ex_info->handlers); ++j) {
|
guint32 val;
|
ex_block = (MonoILExceptionBlock*)mono_array_addr (ex_info->handlers, MonoILExceptionBlock, j);
|
/* the flags */
|
val = GUINT32_TO_LE (ex_block->type);
|
mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32));
|
/* try offset */
|
val = GUINT32_TO_LE (ex_info->start);
|
mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32));
|
/* need fault, too, probably */
|
if (ex_block->type == MONO_EXCEPTION_CLAUSE_FINALLY)
|
val = GUINT32_TO_LE (finally_start - ex_info->start);
|
else
|
val = GUINT32_TO_LE (ex_info->len);
|
mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32));
|
/* handler offset */
|
val = GUINT32_TO_LE (ex_block->start);
|
mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32));
|
/* handler len */
|
val = GUINT32_TO_LE (ex_block->len);
|
mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32));
|
finally_start = ex_block->start + ex_block->len;
|
if (ex_block->extype) {
|
MonoType *extype = mono_reflection_type_get_handle ((MonoReflectionType*)ex_block->extype, error);
|
return_val_if_nok (error, 0);
|
|
val = mono_metadata_token_from_dor (mono_image_typedef_or_ref (assembly, extype));
|
} else {
|
if (ex_block->type == MONO_EXCEPTION_CLAUSE_FILTER)
|
val = ex_block->filter_offset;
|
else
|
val = 0;
|
}
|
val = GUINT32_TO_LE (val);
|
mono_image_add_stream_data (&assembly->code, (char*)&val, sizeof (guint32));
|
/*g_print ("out clause %d: from %d len=%d, handler at %d, %d, finally_start=%d, ex_info->start=%d, ex_info->len=%d, ex_block->type=%d, j=%d, i=%d\n",
|
clause.flags, clause.try_offset, clause.try_len, clause.handler_offset, clause.handler_len, finally_start, ex_info->start, ex_info->len, ex_block->type, j, i);*/
|
}
|
} else {
|
g_error ("No clauses for ex info block %d", i);
|
}
|
}
|
}
|
return assembly->text_rva + idx;
|
}
|
|
/*
|
* Fill in the MethodDef and ParamDef tables for a method.
|
* This is used for both normal methods and constructors.
|
*/
|
static gboolean
|
mono_image_basic_method (ReflectionMethodBuilder *mb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint i, count;
|
|
error_init (error);
|
|
/* room in this table is already allocated */
|
table = &assembly->tables [MONO_TABLE_METHOD];
|
*mb->table_idx = table->next_idx ++;
|
g_hash_table_insert (assembly->method_to_table_idx, mb->mhandle, GUINT_TO_POINTER ((*mb->table_idx)));
|
values = table->values + *mb->table_idx * MONO_METHOD_SIZE;
|
values [MONO_METHOD_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->name, error);
|
return_val_if_nok (error, FALSE);
|
values [MONO_METHOD_FLAGS] = mb->attrs;
|
values [MONO_METHOD_IMPLFLAGS] = mb->iattrs;
|
values [MONO_METHOD_SIGNATURE] = mono_dynimage_encode_method_builder_signature (assembly, mb, error);
|
return_val_if_nok (error, FALSE);
|
values [MONO_METHOD_RVA] = method_encode_code (assembly, mb, error);
|
return_val_if_nok (error, FALSE);
|
|
table = &assembly->tables [MONO_TABLE_PARAM];
|
values [MONO_METHOD_PARAMLIST] = table->next_idx;
|
|
mono_image_add_decl_security (assembly,
|
mono_metadata_make_token (MONO_TABLE_METHOD, *mb->table_idx), mb->permissions);
|
|
if (mb->pinfo) {
|
MonoDynamicTable *mtable;
|
guint32 *mvalues;
|
|
mtable = &assembly->tables [MONO_TABLE_FIELDMARSHAL];
|
mvalues = mtable->values + mtable->next_idx * MONO_FIELD_MARSHAL_SIZE;
|
|
count = 0;
|
for (i = 0; i < mono_array_length (mb->pinfo); ++i) {
|
if (mono_array_get (mb->pinfo, gpointer, i))
|
count++;
|
}
|
table->rows += count;
|
alloc_table (table, table->rows);
|
values = table->values + table->next_idx * MONO_PARAM_SIZE;
|
for (i = 0; i < mono_array_length (mb->pinfo); ++i) {
|
MonoReflectionParamBuilder *pb;
|
if ((pb = mono_array_get (mb->pinfo, MonoReflectionParamBuilder*, i))) {
|
values [MONO_PARAM_FLAGS] = pb->attrs;
|
values [MONO_PARAM_SEQUENCE] = i;
|
if (pb->name != NULL) {
|
values [MONO_PARAM_NAME] = string_heap_insert_mstring (&assembly->sheap, pb->name, error);
|
return_val_if_nok (error, FALSE);
|
} else {
|
values [MONO_PARAM_NAME] = 0;
|
}
|
values += MONO_PARAM_SIZE;
|
if (pb->marshal_info) {
|
mtable->rows++;
|
alloc_table (mtable, mtable->rows);
|
mvalues = mtable->values + mtable->rows * MONO_FIELD_MARSHAL_SIZE;
|
mvalues [MONO_FIELD_MARSHAL_PARENT] = (table->next_idx << MONO_HAS_FIELD_MARSHAL_BITS) | MONO_HAS_FIELD_MARSHAL_PARAMDEF;
|
mvalues [MONO_FIELD_MARSHAL_NATIVE_TYPE] = mono_dynimage_save_encode_marshal_blob (assembly, pb->marshal_info, error);
|
return_val_if_nok (error, FALSE);
|
}
|
pb->table_idx = table->next_idx++;
|
if (pb->attrs & PARAM_ATTRIBUTE_HAS_DEFAULT) {
|
guint32 field_type = 0;
|
mtable = &assembly->tables [MONO_TABLE_CONSTANT];
|
mtable->rows ++;
|
alloc_table (mtable, mtable->rows);
|
mvalues = mtable->values + mtable->rows * MONO_CONSTANT_SIZE;
|
mvalues [MONO_CONSTANT_PARENT] = MONO_HASCONSTANT_PARAM | (pb->table_idx << MONO_HASCONSTANT_BITS);
|
mvalues [MONO_CONSTANT_VALUE] = mono_dynimage_encode_constant (assembly, pb->def_value, &field_type);
|
mvalues [MONO_CONSTANT_TYPE] = field_type;
|
mvalues [MONO_CONSTANT_PADDING] = 0;
|
}
|
}
|
}
|
}
|
|
return TRUE;
|
}
|
|
static gboolean
|
mono_image_add_methodimpl (MonoDynamicImage *assembly, MonoReflectionMethodBuilder *mb, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mb->type;
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint32 tok;
|
MonoReflectionMethod *m;
|
int i;
|
|
error_init (error);
|
|
if (!mb->override_methods)
|
return TRUE;
|
|
for (i = 0; i < mono_array_length (mb->override_methods); ++i) {
|
m = mono_array_get (mb->override_methods, MonoReflectionMethod*, i);
|
|
table = &assembly->tables [MONO_TABLE_METHODIMPL];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_METHODIMPL_SIZE;
|
values [MONO_METHODIMPL_CLASS] = tb->table_idx;
|
values [MONO_METHODIMPL_BODY] = MONO_METHODDEFORREF_METHODDEF | (mb->table_idx << MONO_METHODDEFORREF_BITS);
|
|
tok = image_create_token_raw (assembly, (MonoObject*)m, FALSE, FALSE, error); /* FIXME use handles */
|
return_val_if_nok (error, FALSE);
|
|
switch (mono_metadata_token_table (tok)) {
|
case MONO_TABLE_MEMBERREF:
|
tok = (mono_metadata_token_index (tok) << MONO_METHODDEFORREF_BITS ) | MONO_METHODDEFORREF_METHODREF;
|
break;
|
case MONO_TABLE_METHOD:
|
tok = (mono_metadata_token_index (tok) << MONO_METHODDEFORREF_BITS ) | MONO_METHODDEFORREF_METHODDEF;
|
break;
|
default:
|
g_assert_not_reached ();
|
}
|
values [MONO_METHODIMPL_DECLARATION] = tok;
|
}
|
|
return TRUE;
|
}
|
|
#ifndef DISABLE_REFLECTION_EMIT
|
static gboolean
|
mono_image_get_method_info (MonoReflectionMethodBuilder *mb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
MonoDynamicTable *table;
|
guint32 *values;
|
ReflectionMethodBuilder rmb;
|
int i;
|
|
error_init (error);
|
|
if (!mono_reflection_methodbuilder_from_method_builder (&rmb, mb, error) ||
|
!mono_image_basic_method (&rmb, assembly, error))
|
return FALSE;
|
|
mb->table_idx = *rmb.table_idx;
|
|
if (mb->dll) { /* It's a P/Invoke method */
|
guint32 moduleref;
|
/* map CharSet values to on-disk values */
|
int ncharset = (mb->charset ? (mb->charset - 1) * 2 : 0);
|
int extra_flags = mb->extra_flags;
|
table = &assembly->tables [MONO_TABLE_IMPLMAP];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_IMPLMAP_SIZE;
|
|
values [MONO_IMPLMAP_FLAGS] = (mb->native_cc << 8) | ncharset | extra_flags;
|
values [MONO_IMPLMAP_MEMBER] = (mb->table_idx << 1) | 1; /* memberforwarded: method */
|
if (mb->dllentry) {
|
values [MONO_IMPLMAP_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->dllentry, error);
|
return_val_if_nok (error, FALSE);
|
} else {
|
values [MONO_IMPLMAP_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->name, error);
|
return_val_if_nok (error, FALSE);
|
}
|
moduleref = string_heap_insert_mstring (&assembly->sheap, mb->dll, error);
|
return_val_if_nok (error, FALSE);
|
if (!(values [MONO_IMPLMAP_SCOPE] = find_index_in_table (assembly, MONO_TABLE_MODULEREF, MONO_MODULEREF_NAME, moduleref))) {
|
table = &assembly->tables [MONO_TABLE_MODULEREF];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
table->values [table->rows * MONO_MODULEREF_SIZE + MONO_MODULEREF_NAME] = moduleref;
|
values [MONO_IMPLMAP_SCOPE] = table->rows;
|
}
|
}
|
|
if (mb->generic_params) {
|
table = &assembly->tables [MONO_TABLE_GENERICPARAM];
|
table->rows += mono_array_length (mb->generic_params);
|
alloc_table (table, table->rows);
|
for (i = 0; i < mono_array_length (mb->generic_params); ++i) {
|
guint32 owner = MONO_TYPEORMETHOD_METHOD | (mb->table_idx << MONO_TYPEORMETHOD_BITS);
|
|
mono_image_get_generic_param_info (
|
(MonoReflectionGenericParam *)mono_array_get (mb->generic_params, gpointer, i), owner, assembly);
|
}
|
}
|
|
return TRUE;
|
}
|
|
static gboolean
|
mono_image_get_ctor_info (MonoDomain *domain, MonoReflectionCtorBuilder *mb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
ReflectionMethodBuilder rmb;
|
|
if (!mono_reflection_methodbuilder_from_ctor_builder (&rmb, mb, error))
|
return FALSE;
|
|
if (!mono_image_basic_method (&rmb, assembly, error))
|
return FALSE;
|
|
mb->table_idx = *rmb.table_idx;
|
|
return TRUE;
|
}
|
#endif
|
|
static void
|
mono_image_get_field_info (MonoReflectionFieldBuilder *fb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
error_init (error);
|
|
MonoDynamicTable *table;
|
guint32 *values;
|
|
/* maybe this fixup should be done in the C# code */
|
if (fb->attrs & FIELD_ATTRIBUTE_LITERAL)
|
fb->attrs |= FIELD_ATTRIBUTE_HAS_DEFAULT;
|
table = &assembly->tables [MONO_TABLE_FIELD];
|
fb->table_idx = table->next_idx ++;
|
g_hash_table_insert (assembly->field_to_table_idx, fb->handle, GUINT_TO_POINTER (fb->table_idx));
|
values = table->values + fb->table_idx * MONO_FIELD_SIZE;
|
values [MONO_FIELD_NAME] = string_heap_insert_mstring (&assembly->sheap, fb->name, error);
|
return_if_nok (error);
|
values [MONO_FIELD_FLAGS] = fb->attrs;
|
values [MONO_FIELD_SIGNATURE] = mono_dynimage_encode_field_signature (assembly, fb, error);
|
return_if_nok (error);
|
|
if (fb->offset != -1) {
|
table = &assembly->tables [MONO_TABLE_FIELDLAYOUT];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_FIELD_LAYOUT_SIZE;
|
values [MONO_FIELD_LAYOUT_FIELD] = fb->table_idx;
|
values [MONO_FIELD_LAYOUT_OFFSET] = fb->offset;
|
}
|
if (fb->attrs & FIELD_ATTRIBUTE_LITERAL) {
|
MonoTypeEnum field_type = (MonoTypeEnum)0;
|
table = &assembly->tables [MONO_TABLE_CONSTANT];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_CONSTANT_SIZE;
|
values [MONO_CONSTANT_PARENT] = MONO_HASCONSTANT_FIEDDEF | (fb->table_idx << MONO_HASCONSTANT_BITS);
|
values [MONO_CONSTANT_VALUE] = mono_dynimage_encode_constant (assembly, fb->def_value, &field_type);
|
values [MONO_CONSTANT_TYPE] = field_type;
|
values [MONO_CONSTANT_PADDING] = 0;
|
}
|
if (fb->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) {
|
guint32 rva_idx;
|
table = &assembly->tables [MONO_TABLE_FIELDRVA];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_FIELD_RVA_SIZE;
|
values [MONO_FIELD_RVA_FIELD] = fb->table_idx;
|
/*
|
* We store it in the code section because it's simpler for now.
|
*/
|
if (fb->rva_data) {
|
if (mono_array_length (fb->rva_data) >= 10)
|
stream_data_align (&assembly->code);
|
rva_idx = mono_image_add_stream_data (&assembly->code, mono_array_addr (fb->rva_data, char, 0), mono_array_length (fb->rva_data));
|
} else
|
rva_idx = mono_image_add_stream_zero (&assembly->code, mono_class_value_size (fb->handle->parent, NULL));
|
values [MONO_FIELD_RVA_RVA] = rva_idx + assembly->text_rva;
|
}
|
if (fb->marshal_info) {
|
table = &assembly->tables [MONO_TABLE_FIELDMARSHAL];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_FIELD_MARSHAL_SIZE;
|
values [MONO_FIELD_MARSHAL_PARENT] = (fb->table_idx << MONO_HAS_FIELD_MARSHAL_BITS) | MONO_HAS_FIELD_MARSHAL_FIELDSREF;
|
values [MONO_FIELD_MARSHAL_NATIVE_TYPE] = mono_dynimage_save_encode_marshal_blob (assembly, fb->marshal_info, error);
|
return_if_nok (error);
|
}
|
}
|
|
static void
|
mono_image_get_property_info (MonoReflectionPropertyBuilder *pb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
error_init (error);
|
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint num_methods = 0;
|
guint32 semaidx;
|
|
/*
|
* we need to set things in the following tables:
|
* PROPERTYMAP (info already filled in _get_type_info ())
|
* PROPERTY (rows already preallocated in _get_type_info ())
|
* METHOD (method info already done with the generic method code)
|
* METHODSEMANTICS
|
* CONSTANT
|
*/
|
table = &assembly->tables [MONO_TABLE_PROPERTY];
|
pb->table_idx = table->next_idx ++;
|
values = table->values + pb->table_idx * MONO_PROPERTY_SIZE;
|
values [MONO_PROPERTY_NAME] = string_heap_insert_mstring (&assembly->sheap, pb->name, error);
|
return_if_nok (error);
|
values [MONO_PROPERTY_FLAGS] = pb->attrs;
|
values [MONO_PROPERTY_TYPE] = mono_dynimage_save_encode_property_signature (assembly, pb, error);
|
return_if_nok (error);
|
|
|
/* FIXME: we still don't handle 'other' methods */
|
if (pb->get_method) num_methods ++;
|
if (pb->set_method) num_methods ++;
|
|
table = &assembly->tables [MONO_TABLE_METHODSEMANTICS];
|
table->rows += num_methods;
|
alloc_table (table, table->rows);
|
|
if (pb->get_method) {
|
semaidx = table->next_idx ++;
|
values = table->values + semaidx * MONO_METHOD_SEMA_SIZE;
|
values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_GETTER;
|
values [MONO_METHOD_SEMA_METHOD] = pb->get_method->table_idx;
|
values [MONO_METHOD_SEMA_ASSOCIATION] = (pb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_PROPERTY;
|
}
|
if (pb->set_method) {
|
semaidx = table->next_idx ++;
|
values = table->values + semaidx * MONO_METHOD_SEMA_SIZE;
|
values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_SETTER;
|
values [MONO_METHOD_SEMA_METHOD] = pb->set_method->table_idx;
|
values [MONO_METHOD_SEMA_ASSOCIATION] = (pb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_PROPERTY;
|
}
|
if (pb->attrs & PROPERTY_ATTRIBUTE_HAS_DEFAULT) {
|
MonoTypeEnum field_type = (MonoTypeEnum)0;
|
table = &assembly->tables [MONO_TABLE_CONSTANT];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_CONSTANT_SIZE;
|
values [MONO_CONSTANT_PARENT] = MONO_HASCONSTANT_PROPERTY | (pb->table_idx << MONO_HASCONSTANT_BITS);
|
values [MONO_CONSTANT_VALUE] = mono_dynimage_encode_constant (assembly, pb->def_value, &field_type);
|
values [MONO_CONSTANT_TYPE] = field_type;
|
values [MONO_CONSTANT_PADDING] = 0;
|
}
|
}
|
|
static void
|
mono_image_get_event_info (MonoReflectionEventBuilder *eb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint num_methods = 0;
|
guint32 semaidx;
|
|
/*
|
* we need to set things in the following tables:
|
* EVENTMAP (info already filled in _get_type_info ())
|
* EVENT (rows already preallocated in _get_type_info ())
|
* METHOD (method info already done with the generic method code)
|
* METHODSEMANTICS
|
*/
|
table = &assembly->tables [MONO_TABLE_EVENT];
|
eb->table_idx = table->next_idx ++;
|
values = table->values + eb->table_idx * MONO_EVENT_SIZE;
|
values [MONO_EVENT_NAME] = string_heap_insert_mstring (&assembly->sheap, eb->name, error);
|
return_if_nok (error);
|
values [MONO_EVENT_FLAGS] = eb->attrs;
|
MonoType *ebtype = mono_reflection_type_get_handle (eb->type, error);
|
return_if_nok (error);
|
values [MONO_EVENT_TYPE] = mono_image_typedef_or_ref (assembly, ebtype);
|
|
/*
|
* FIXME: we still don't handle 'other' methods
|
*/
|
if (eb->add_method) num_methods ++;
|
if (eb->remove_method) num_methods ++;
|
if (eb->raise_method) num_methods ++;
|
|
table = &assembly->tables [MONO_TABLE_METHODSEMANTICS];
|
table->rows += num_methods;
|
alloc_table (table, table->rows);
|
|
if (eb->add_method) {
|
semaidx = table->next_idx ++;
|
values = table->values + semaidx * MONO_METHOD_SEMA_SIZE;
|
values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_ADD_ON;
|
values [MONO_METHOD_SEMA_METHOD] = eb->add_method->table_idx;
|
values [MONO_METHOD_SEMA_ASSOCIATION] = (eb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT;
|
}
|
if (eb->remove_method) {
|
semaidx = table->next_idx ++;
|
values = table->values + semaidx * MONO_METHOD_SEMA_SIZE;
|
values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_REMOVE_ON;
|
values [MONO_METHOD_SEMA_METHOD] = eb->remove_method->table_idx;
|
values [MONO_METHOD_SEMA_ASSOCIATION] = (eb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT;
|
}
|
if (eb->raise_method) {
|
semaidx = table->next_idx ++;
|
values = table->values + semaidx * MONO_METHOD_SEMA_SIZE;
|
values [MONO_METHOD_SEMA_SEMANTICS] = METHOD_SEMANTIC_FIRE;
|
values [MONO_METHOD_SEMA_METHOD] = eb->raise_method->table_idx;
|
values [MONO_METHOD_SEMA_ASSOCIATION] = (eb->table_idx << MONO_HAS_SEMANTICS_BITS) | MONO_HAS_SEMANTICS_EVENT;
|
}
|
}
|
|
static void
|
encode_constraints (MonoReflectionGenericParam *gparam, guint32 owner, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
error_init (error);
|
|
MonoDynamicTable *table;
|
guint32 num_constraints, i;
|
guint32 *values;
|
guint32 table_idx;
|
|
table = &assembly->tables [MONO_TABLE_GENERICPARAMCONSTRAINT];
|
num_constraints = gparam->iface_constraints ?
|
mono_array_length (gparam->iface_constraints) : 0;
|
table->rows += num_constraints;
|
if (gparam->base_type)
|
table->rows++;
|
alloc_table (table, table->rows);
|
|
if (gparam->base_type) {
|
table_idx = table->next_idx ++;
|
values = table->values + table_idx * MONO_GENPARCONSTRAINT_SIZE;
|
|
MonoType *gpbasetype = mono_reflection_type_get_handle (gparam->base_type, error);
|
return_if_nok (error);
|
values [MONO_GENPARCONSTRAINT_GENERICPAR] = owner;
|
values [MONO_GENPARCONSTRAINT_CONSTRAINT] = mono_image_typedef_or_ref (assembly, gpbasetype);
|
}
|
|
for (i = 0; i < num_constraints; i++) {
|
MonoReflectionType *constraint = (MonoReflectionType *)mono_array_get (
|
gparam->iface_constraints, gpointer, i);
|
|
table_idx = table->next_idx ++;
|
values = table->values + table_idx * MONO_GENPARCONSTRAINT_SIZE;
|
|
MonoType *constraint_type = mono_reflection_type_get_handle (constraint, error);
|
return_if_nok (error);
|
|
values [MONO_GENPARCONSTRAINT_GENERICPAR] = owner;
|
values [MONO_GENPARCONSTRAINT_CONSTRAINT] = mono_image_typedef_or_ref (assembly, constraint_type);
|
}
|
}
|
|
static void
|
mono_image_get_generic_param_info (MonoReflectionGenericParam *gparam, guint32 owner, MonoDynamicImage *assembly)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
GenericParamTableEntry *entry;
|
|
/*
|
* The GenericParam table must be sorted according to the `owner' field.
|
* We need to do this sorting prior to writing the GenericParamConstraint
|
* table, since we have to use the final GenericParam table indices there
|
* and they must also be sorted.
|
*/
|
|
entry = g_new0 (GenericParamTableEntry, 1);
|
entry->owner = owner;
|
/* FIXME: track where gen_params should be freed and remove the GC root as well */
|
MONO_GC_REGISTER_ROOT_IF_MOVING (entry->gparam, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Generic Parameter");
|
entry->gparam = gparam;
|
|
g_ptr_array_add (assembly->gen_params, entry);
|
}
|
|
static gboolean
|
write_generic_param_entry (MonoDynamicImage *assembly, GenericParamTableEntry *entry, MonoError *error)
|
{
|
MONO_REQ_GC_UNSAFE_MODE;
|
|
MonoDynamicTable *table;
|
MonoGenericParam *param;
|
guint32 *values;
|
guint32 table_idx;
|
|
error_init (error);
|
|
table = &assembly->tables [MONO_TABLE_GENERICPARAM];
|
table_idx = table->next_idx ++;
|
values = table->values + table_idx * MONO_GENERICPARAM_SIZE;
|
|
MonoType *gparam_type = mono_reflection_type_get_handle ((MonoReflectionType*)entry->gparam, error);
|
return_val_if_nok (error, FALSE);
|
|
param = gparam_type->data.generic_param;
|
|
values [MONO_GENERICPARAM_OWNER] = entry->owner;
|
values [MONO_GENERICPARAM_FLAGS] = entry->gparam->attrs;
|
values [MONO_GENERICPARAM_NUMBER] = mono_generic_param_num (param);
|
values [MONO_GENERICPARAM_NAME] = string_heap_insert (&assembly->sheap, mono_generic_param_info (param)->name);
|
|
if (!mono_image_add_cattrs (assembly, table_idx, MONO_CUSTOM_ATTR_GENERICPAR, entry->gparam->cattrs, error))
|
return FALSE;
|
|
encode_constraints (entry->gparam, table_idx, assembly, error);
|
return_val_if_nok (error, FALSE);
|
|
return TRUE;
|
}
|
|
static void
|
collect_types (MonoPtrArray *types, MonoReflectionTypeBuilder *type)
|
{
|
int i;
|
|
mono_ptr_array_append (*types, type);
|
|
if (!type->subtypes)
|
return;
|
|
for (i = 0; i < mono_array_length (type->subtypes); ++i) {
|
MonoReflectionTypeBuilder *subtype = mono_array_get (type->subtypes, MonoReflectionTypeBuilder*, i);
|
collect_types (types, subtype);
|
}
|
}
|
|
static gint
|
compare_types_by_table_idx (MonoReflectionTypeBuilder **type1, MonoReflectionTypeBuilder **type2)
|
{
|
if ((*type1)->table_idx < (*type2)->table_idx)
|
return -1;
|
else
|
if ((*type1)->table_idx > (*type2)->table_idx)
|
return 1;
|
else
|
return 0;
|
}
|
|
static gboolean
|
params_add_cattrs (MonoDynamicImage *assembly, MonoArray *pinfo, MonoError *error) {
|
int i;
|
|
error_init (error);
|
if (!pinfo)
|
return TRUE;
|
for (i = 0; i < mono_array_length (pinfo); ++i) {
|
MonoReflectionParamBuilder *pb;
|
pb = mono_array_get (pinfo, MonoReflectionParamBuilder *, i);
|
if (!pb)
|
continue;
|
if (!mono_image_add_cattrs (assembly, pb->table_idx, MONO_CUSTOM_ATTR_PARAMDEF, pb->cattrs, error))
|
return FALSE;
|
}
|
|
return TRUE;
|
}
|
|
static gboolean
|
type_add_cattrs (MonoDynamicImage *assembly, MonoReflectionTypeBuilder *tb, MonoError *error) {
|
int i;
|
|
error_init (error);
|
|
if (!mono_image_add_cattrs (assembly, tb->table_idx, MONO_CUSTOM_ATTR_TYPEDEF, tb->cattrs, error))
|
return FALSE;
|
if (tb->fields) {
|
for (i = 0; i < tb->num_fields; ++i) {
|
MonoReflectionFieldBuilder* fb;
|
fb = mono_array_get (tb->fields, MonoReflectionFieldBuilder*, i);
|
if (!mono_image_add_cattrs (assembly, fb->table_idx, MONO_CUSTOM_ATTR_FIELDDEF, fb->cattrs, error))
|
return FALSE;
|
}
|
}
|
if (tb->events) {
|
for (i = 0; i < mono_array_length (tb->events); ++i) {
|
MonoReflectionEventBuilder* eb;
|
eb = mono_array_get (tb->events, MonoReflectionEventBuilder*, i);
|
if (!mono_image_add_cattrs (assembly, eb->table_idx, MONO_CUSTOM_ATTR_EVENT, eb->cattrs, error))
|
return FALSE;
|
}
|
}
|
if (tb->properties) {
|
for (i = 0; i < mono_array_length (tb->properties); ++i) {
|
MonoReflectionPropertyBuilder* pb;
|
pb = mono_array_get (tb->properties, MonoReflectionPropertyBuilder*, i);
|
if (!mono_image_add_cattrs (assembly, pb->table_idx, MONO_CUSTOM_ATTR_PROPERTY, pb->cattrs, error))
|
return FALSE;
|
}
|
}
|
if (tb->ctors) {
|
for (i = 0; i < mono_array_length (tb->ctors); ++i) {
|
MonoReflectionCtorBuilder* cb;
|
cb = mono_array_get (tb->ctors, MonoReflectionCtorBuilder*, i);
|
if (!mono_image_add_cattrs (assembly, cb->table_idx, MONO_CUSTOM_ATTR_METHODDEF, cb->cattrs, error) ||
|
!params_add_cattrs (assembly, cb->pinfo, error))
|
return FALSE;
|
}
|
}
|
|
if (tb->methods) {
|
for (i = 0; i < tb->num_methods; ++i) {
|
MonoReflectionMethodBuilder* mb;
|
mb = mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i);
|
if (!mono_image_add_cattrs (assembly, mb->table_idx, MONO_CUSTOM_ATTR_METHODDEF, mb->cattrs, error) ||
|
!params_add_cattrs (assembly, mb->pinfo, error))
|
return FALSE;
|
}
|
}
|
|
if (tb->subtypes) {
|
for (i = 0; i < mono_array_length (tb->subtypes); ++i) {
|
if (!type_add_cattrs (assembly, mono_array_get (tb->subtypes, MonoReflectionTypeBuilder*, i), error))
|
return FALSE;
|
}
|
}
|
|
return TRUE;
|
}
|
|
static gboolean
|
module_add_cattrs (MonoDynamicImage *assembly, MonoReflectionModuleBuilder *moduleb, MonoError *error)
|
{
|
int i;
|
|
error_init (error);
|
|
if (!mono_image_add_cattrs (assembly, moduleb->table_idx, MONO_CUSTOM_ATTR_MODULE, moduleb->cattrs, error))
|
return FALSE;
|
|
if (moduleb->global_methods) {
|
for (i = 0; i < mono_array_length (moduleb->global_methods); ++i) {
|
MonoReflectionMethodBuilder* mb = mono_array_get (moduleb->global_methods, MonoReflectionMethodBuilder*, i);
|
if (!mono_image_add_cattrs (assembly, mb->table_idx, MONO_CUSTOM_ATTR_METHODDEF, mb->cattrs, error) ||
|
!params_add_cattrs (assembly, mb->pinfo, error))
|
return FALSE;
|
}
|
}
|
|
if (moduleb->global_fields) {
|
for (i = 0; i < mono_array_length (moduleb->global_fields); ++i) {
|
MonoReflectionFieldBuilder *fb = mono_array_get (moduleb->global_fields, MonoReflectionFieldBuilder*, i);
|
if (!mono_image_add_cattrs (assembly, fb->table_idx, MONO_CUSTOM_ATTR_FIELDDEF, fb->cattrs, error))
|
return FALSE;
|
}
|
}
|
|
if (moduleb->types) {
|
for (i = 0; i < moduleb->num_types; ++i) {
|
if (!type_add_cattrs (assembly, mono_array_get (moduleb->types, MonoReflectionTypeBuilder*, i), error))
|
return FALSE;
|
}
|
}
|
|
return TRUE;
|
}
|
|
static gboolean
|
mono_image_fill_file_table (MonoDomain *domain, MonoReflectionModule *module, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
guint32 *values;
|
char blob_size [6];
|
guchar hash [20];
|
char *b = blob_size;
|
char *dir, *path;
|
|
error_init (error);
|
|
table = &assembly->tables [MONO_TABLE_FILE];
|
table->rows++;
|
alloc_table (table, table->rows);
|
values = table->values + table->next_idx * MONO_FILE_SIZE;
|
values [MONO_FILE_FLAGS] = FILE_CONTAINS_METADATA;
|
values [MONO_FILE_NAME] = string_heap_insert (&assembly->sheap, module->image->module_name);
|
if (image_is_dynamic (module->image)) {
|
/* This depends on the fact that the main module is emitted last */
|
dir = mono_string_to_utf8_checked (((MonoReflectionModuleBuilder*)module)->assemblyb->dir, error);
|
return_val_if_nok (error, FALSE);
|
path = g_strdup_printf ("%s%c%s", dir, G_DIR_SEPARATOR, module->image->module_name);
|
} else {
|
dir = NULL;
|
path = g_strdup (module->image->name);
|
}
|
mono_sha1_get_digest_from_file (path, hash);
|
g_free (dir);
|
g_free (path);
|
mono_metadata_encode_value (20, b, &b);
|
values [MONO_FILE_HASH_VALUE] = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size);
|
mono_image_add_stream_data (&assembly->blob, (char*)hash, 20);
|
table->next_idx ++;
|
return TRUE;
|
}
|
|
static void
|
mono_image_fill_module_table (MonoDomain *domain, MonoReflectionModuleBuilder *mb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
int i;
|
|
error_init (error);
|
|
table = &assembly->tables [MONO_TABLE_MODULE];
|
mb->table_idx = table->next_idx ++;
|
table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_NAME] = string_heap_insert_mstring (&assembly->sheap, mb->module.name, error);
|
return_if_nok (error);
|
i = mono_image_add_stream_data (&assembly->guid, mono_array_addr (mb->guid, char, 0), 16);
|
i /= 16;
|
++i;
|
table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_GENERATION] = 0;
|
table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_MVID] = i;
|
table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_ENC] = 0;
|
table->values [mb->table_idx * MONO_MODULE_SIZE + MONO_MODULE_ENCBASE] = 0;
|
}
|
|
static guint32
|
mono_image_fill_export_table_from_class (MonoDomain *domain, MonoClass *klass,
|
guint32 module_index, guint32 parent_index, MonoDynamicImage *assembly)
|
{
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint32 visib, res;
|
|
visib = mono_class_get_flags (klass) & TYPE_ATTRIBUTE_VISIBILITY_MASK;
|
if (! ((visib & TYPE_ATTRIBUTE_PUBLIC) || (visib & TYPE_ATTRIBUTE_NESTED_PUBLIC)))
|
return 0;
|
|
table = &assembly->tables [MONO_TABLE_EXPORTEDTYPE];
|
table->rows++;
|
alloc_table (table, table->rows);
|
values = table->values + table->next_idx * MONO_EXP_TYPE_SIZE;
|
|
values [MONO_EXP_TYPE_FLAGS] = mono_class_get_flags (klass);
|
values [MONO_EXP_TYPE_TYPEDEF] = klass->type_token;
|
if (klass->nested_in)
|
values [MONO_EXP_TYPE_IMPLEMENTATION] = (parent_index << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_EXP_TYPE;
|
else
|
values [MONO_EXP_TYPE_IMPLEMENTATION] = (module_index << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_FILE;
|
values [MONO_EXP_TYPE_NAME] = string_heap_insert (&assembly->sheap, klass->name);
|
values [MONO_EXP_TYPE_NAMESPACE] = string_heap_insert (&assembly->sheap, klass->name_space);
|
|
res = table->next_idx;
|
|
table->next_idx ++;
|
|
/* Emit nested types */
|
GList *nested_classes = mono_class_get_nested_classes_property (klass);
|
GList *tmp;
|
for (tmp = nested_classes; tmp; tmp = tmp->next)
|
mono_image_fill_export_table_from_class (domain, (MonoClass *)tmp->data, module_index, table->next_idx - 1, assembly);
|
|
return res;
|
}
|
|
static void
|
mono_image_fill_export_table (MonoDomain *domain, MonoReflectionTypeBuilder *tb,
|
guint32 module_index, guint32 parent_index, MonoDynamicImage *assembly,
|
MonoError *error)
|
{
|
MonoClass *klass;
|
guint32 idx, i;
|
|
error_init (error);
|
|
MonoType *t = mono_reflection_type_get_handle ((MonoReflectionType*)tb, error);
|
return_if_nok (error);
|
|
klass = mono_class_from_mono_type (t);
|
|
klass->type_token = mono_metadata_make_token (MONO_TABLE_TYPEDEF, tb->table_idx);
|
|
idx = mono_image_fill_export_table_from_class (domain, klass, module_index,
|
parent_index, assembly);
|
|
/*
|
* Emit nested types
|
* We need to do this ourselves since klass->nested_classes is not set up.
|
*/
|
if (tb->subtypes) {
|
for (i = 0; i < mono_array_length (tb->subtypes); ++i) {
|
mono_image_fill_export_table (domain, mono_array_get (tb->subtypes, MonoReflectionTypeBuilder*, i), module_index, idx, assembly, error);
|
return_if_nok (error);
|
}
|
}
|
}
|
|
static void
|
mono_image_fill_export_table_from_module (MonoDomain *domain, MonoReflectionModule *module,
|
guint32 module_index, MonoDynamicImage *assembly)
|
{
|
MonoImage *image = module->image;
|
MonoTableInfo *t;
|
guint32 i;
|
|
t = &image->tables [MONO_TABLE_TYPEDEF];
|
|
for (i = 0; i < t->rows; ++i) {
|
MonoError error;
|
MonoClass *klass = mono_class_get_checked (image, mono_metadata_make_token (MONO_TABLE_TYPEDEF, i + 1), &error);
|
g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
|
|
if (mono_class_is_public (klass))
|
mono_image_fill_export_table_from_class (domain, klass, module_index, 0, assembly);
|
}
|
}
|
|
static void
|
add_exported_type (MonoReflectionAssemblyBuilder *assemblyb, MonoDynamicImage *assembly, MonoClass *klass, guint32 parent_index)
|
{
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint32 scope, scope_idx, impl, current_idx;
|
gboolean forwarder = TRUE;
|
gpointer iter = NULL;
|
MonoClass *nested;
|
|
if (klass->nested_in) {
|
impl = (parent_index << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_EXP_TYPE;
|
forwarder = FALSE;
|
} else {
|
scope = mono_reflection_resolution_scope_from_image (assembly, klass->image);
|
g_assert ((scope & MONO_RESOLUTION_SCOPE_MASK) == MONO_RESOLUTION_SCOPE_ASSEMBLYREF);
|
scope_idx = scope >> MONO_RESOLUTION_SCOPE_BITS;
|
impl = (scope_idx << MONO_IMPLEMENTATION_BITS) + MONO_IMPLEMENTATION_ASSEMBLYREF;
|
}
|
|
table = &assembly->tables [MONO_TABLE_EXPORTEDTYPE];
|
|
table->rows++;
|
alloc_table (table, table->rows);
|
current_idx = table->next_idx;
|
values = table->values + current_idx * MONO_EXP_TYPE_SIZE;
|
|
values [MONO_EXP_TYPE_FLAGS] = forwarder ? TYPE_ATTRIBUTE_FORWARDER : 0;
|
values [MONO_EXP_TYPE_TYPEDEF] = 0;
|
values [MONO_EXP_TYPE_IMPLEMENTATION] = impl;
|
values [MONO_EXP_TYPE_NAME] = string_heap_insert (&assembly->sheap, klass->name);
|
values [MONO_EXP_TYPE_NAMESPACE] = string_heap_insert (&assembly->sheap, klass->name_space);
|
|
table->next_idx++;
|
|
while ((nested = mono_class_get_nested_types (klass, &iter)))
|
add_exported_type (assemblyb, assembly, nested, current_idx);
|
}
|
|
static void
|
mono_image_fill_export_table_from_type_forwarders (MonoReflectionAssemblyBuilder *assemblyb, MonoDynamicImage *assembly)
|
{
|
MonoError error;
|
MonoClass *klass;
|
int i;
|
|
if (!assemblyb->type_forwarders)
|
return;
|
|
for (i = 0; i < mono_array_length (assemblyb->type_forwarders); ++i) {
|
MonoReflectionType *t = mono_array_get (assemblyb->type_forwarders, MonoReflectionType *, i);
|
MonoType *type;
|
if (!t)
|
continue;
|
|
type = mono_reflection_type_get_handle (t, &error);
|
mono_error_assert_ok (&error);
|
g_assert (type);
|
|
klass = mono_class_from_mono_type (type);
|
|
add_exported_type (assemblyb, assembly, klass, 0);
|
}
|
}
|
|
#define align_pointer(base,p)\
|
do {\
|
guint32 __diff = (unsigned char*)(p)-(unsigned char*)(base);\
|
if (__diff & 3)\
|
(p) += 4 - (__diff & 3);\
|
} while (0)
|
|
static int
|
compare_constants (const void *a, const void *b)
|
{
|
const guint32 *a_values = (const guint32 *)a;
|
const guint32 *b_values = (const guint32 *)b;
|
return a_values [MONO_CONSTANT_PARENT] - b_values [MONO_CONSTANT_PARENT];
|
}
|
|
static int
|
compare_semantics (const void *a, const void *b)
|
{
|
const guint32 *a_values = (const guint32 *)a;
|
const guint32 *b_values = (const guint32 *)b;
|
int assoc = a_values [MONO_METHOD_SEMA_ASSOCIATION] - b_values [MONO_METHOD_SEMA_ASSOCIATION];
|
if (assoc)
|
return assoc;
|
return a_values [MONO_METHOD_SEMA_SEMANTICS] - b_values [MONO_METHOD_SEMA_SEMANTICS];
|
}
|
|
static int
|
compare_custom_attrs (const void *a, const void *b)
|
{
|
const guint32 *a_values = (const guint32 *)a;
|
const guint32 *b_values = (const guint32 *)b;
|
|
return a_values [MONO_CUSTOM_ATTR_PARENT] - b_values [MONO_CUSTOM_ATTR_PARENT];
|
}
|
|
static int
|
compare_field_marshal (const void *a, const void *b)
|
{
|
const guint32 *a_values = (const guint32 *)a;
|
const guint32 *b_values = (const guint32 *)b;
|
|
return a_values [MONO_FIELD_MARSHAL_PARENT] - b_values [MONO_FIELD_MARSHAL_PARENT];
|
}
|
|
static int
|
compare_nested (const void *a, const void *b)
|
{
|
const guint32 *a_values = (const guint32 *)a;
|
const guint32 *b_values = (const guint32 *)b;
|
|
return a_values [MONO_NESTED_CLASS_NESTED] - b_values [MONO_NESTED_CLASS_NESTED];
|
}
|
|
static int
|
compare_genericparam (const void *a, const void *b)
|
{
|
MonoError error;
|
const GenericParamTableEntry **a_entry = (const GenericParamTableEntry **) a;
|
const GenericParamTableEntry **b_entry = (const GenericParamTableEntry **) b;
|
|
if ((*b_entry)->owner == (*a_entry)->owner) {
|
MonoType *a_type = mono_reflection_type_get_handle ((MonoReflectionType*)(*a_entry)->gparam, &error);
|
mono_error_assert_ok (&error);
|
MonoType *b_type = mono_reflection_type_get_handle ((MonoReflectionType*)(*b_entry)->gparam, &error);
|
mono_error_assert_ok (&error);
|
return
|
mono_type_get_generic_param_num (a_type) -
|
mono_type_get_generic_param_num (b_type);
|
} else
|
return (*a_entry)->owner - (*b_entry)->owner;
|
}
|
|
static int
|
compare_declsecurity_attrs (const void *a, const void *b)
|
{
|
const guint32 *a_values = (const guint32 *)a;
|
const guint32 *b_values = (const guint32 *)b;
|
|
return a_values [MONO_DECL_SECURITY_PARENT] - b_values [MONO_DECL_SECURITY_PARENT];
|
}
|
|
static int
|
compare_interface_impl (const void *a, const void *b)
|
{
|
const guint32 *a_values = (const guint32 *)a;
|
const guint32 *b_values = (const guint32 *)b;
|
|
int klass = a_values [MONO_INTERFACEIMPL_CLASS] - b_values [MONO_INTERFACEIMPL_CLASS];
|
if (klass)
|
return klass;
|
|
return a_values [MONO_INTERFACEIMPL_INTERFACE] - b_values [MONO_INTERFACEIMPL_INTERFACE];
|
}
|
|
struct StreamDesc {
|
const char *name;
|
MonoDynamicStream *stream;
|
};
|
|
/*
|
* build_compressed_metadata() fills in the blob of data that represents the
|
* raw metadata as it will be saved in the PE file. The five streams are output
|
* and the metadata tables are comnpressed from the guint32 array representation,
|
* to the compressed on-disk format.
|
*/
|
static gboolean
|
build_compressed_metadata (MonoDynamicImage *assembly, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
int i;
|
guint64 valid_mask = 0;
|
guint64 sorted_mask;
|
guint32 heapt_size = 0;
|
guint32 meta_size = 256; /* allow for header and other stuff */
|
guint32 table_offset;
|
guint32 ntables = 0;
|
guint64 *int64val;
|
guint32 *int32val;
|
guint16 *int16val;
|
MonoImage *meta;
|
unsigned char *p;
|
struct StreamDesc stream_desc [5];
|
|
error_init (error);
|
|
qsort (assembly->gen_params->pdata, assembly->gen_params->len, sizeof (gpointer), compare_genericparam);
|
for (i = 0; i < assembly->gen_params->len; i++) {
|
GenericParamTableEntry *entry = (GenericParamTableEntry *)g_ptr_array_index (assembly->gen_params, i);
|
if (!write_generic_param_entry (assembly, entry, error))
|
return FALSE;
|
}
|
|
stream_desc [0].name = "#~";
|
stream_desc [0].stream = &assembly->tstream;
|
stream_desc [1].name = "#Strings";
|
stream_desc [1].stream = &assembly->sheap;
|
stream_desc [2].name = "#US";
|
stream_desc [2].stream = &assembly->us;
|
stream_desc [3].name = "#Blob";
|
stream_desc [3].stream = &assembly->blob;
|
stream_desc [4].name = "#GUID";
|
stream_desc [4].stream = &assembly->guid;
|
|
/* tables that are sorted */
|
sorted_mask = ((guint64)1 << MONO_TABLE_CONSTANT) | ((guint64)1 << MONO_TABLE_FIELDMARSHAL)
|
| ((guint64)1 << MONO_TABLE_METHODSEMANTICS) | ((guint64)1 << MONO_TABLE_CLASSLAYOUT)
|
| ((guint64)1 << MONO_TABLE_FIELDLAYOUT) | ((guint64)1 << MONO_TABLE_FIELDRVA)
|
| ((guint64)1 << MONO_TABLE_IMPLMAP) | ((guint64)1 << MONO_TABLE_NESTEDCLASS)
|
| ((guint64)1 << MONO_TABLE_METHODIMPL) | ((guint64)1 << MONO_TABLE_CUSTOMATTRIBUTE)
|
| ((guint64)1 << MONO_TABLE_DECLSECURITY) | ((guint64)1 << MONO_TABLE_GENERICPARAM)
|
| ((guint64)1 << MONO_TABLE_INTERFACEIMPL);
|
|
/* Compute table sizes */
|
/* the MonoImage has already been created in mono_reflection_dynimage_basic_init() */
|
meta = &assembly->image;
|
|
/* sizes should be multiple of 4 */
|
mono_dynstream_data_align (&assembly->blob);
|
mono_dynstream_data_align (&assembly->guid);
|
mono_dynstream_data_align (&assembly->sheap);
|
mono_dynstream_data_align (&assembly->us);
|
|
/* Setup the info used by compute_sizes () */
|
meta->idx_blob_wide = assembly->blob.index >= 65536 ? 1 : 0;
|
meta->idx_guid_wide = assembly->guid.index >= 65536 ? 1 : 0;
|
meta->idx_string_wide = assembly->sheap.index >= 65536 ? 1 : 0;
|
|
meta_size += assembly->blob.index;
|
meta_size += assembly->guid.index;
|
meta_size += assembly->sheap.index;
|
meta_size += assembly->us.index;
|
|
for (i=0; i < MONO_TABLE_NUM; ++i)
|
meta->tables [i].rows = assembly->tables [i].rows;
|
|
for (i = 0; i < MONO_TABLE_NUM; i++){
|
if (meta->tables [i].rows == 0)
|
continue;
|
valid_mask |= (guint64)1 << i;
|
ntables ++;
|
meta->tables [i].row_size = mono_metadata_compute_size (
|
meta, i, &meta->tables [i].size_bitfield);
|
heapt_size += meta->tables [i].row_size * meta->tables [i].rows;
|
}
|
heapt_size += 24; /* #~ header size */
|
heapt_size += ntables * 4;
|
/* make multiple of 4 */
|
heapt_size += 3;
|
heapt_size &= ~3;
|
meta_size += heapt_size;
|
meta->raw_metadata = (char *)g_malloc0 (meta_size);
|
p = (unsigned char*)meta->raw_metadata;
|
/* the metadata signature */
|
*p++ = 'B'; *p++ = 'S'; *p++ = 'J'; *p++ = 'B';
|
/* version numbers and 4 bytes reserved */
|
int16val = (guint16*)p;
|
*int16val++ = GUINT16_TO_LE (meta->md_version_major);
|
*int16val = GUINT16_TO_LE (meta->md_version_minor);
|
p += 8;
|
/* version string */
|
int32val = (guint32*)p;
|
*int32val = GUINT32_TO_LE ((strlen (meta->version) + 3) & (~3)); /* needs to be multiple of 4 */
|
p += 4;
|
memcpy (p, meta->version, strlen (meta->version));
|
p += GUINT32_FROM_LE (*int32val);
|
align_pointer (meta->raw_metadata, p);
|
int16val = (guint16*)p;
|
*int16val++ = GUINT16_TO_LE (0); /* flags must be 0 */
|
*int16val = GUINT16_TO_LE (5); /* number of streams */
|
p += 4;
|
|
/*
|
* write the stream info.
|
*/
|
table_offset = (p - (unsigned char*)meta->raw_metadata) + 5 * 8 + 40; /* room needed for stream headers */
|
table_offset += 3; table_offset &= ~3;
|
|
assembly->tstream.index = heapt_size;
|
for (i = 0; i < 5; ++i) {
|
int32val = (guint32*)p;
|
stream_desc [i].stream->offset = table_offset;
|
*int32val++ = GUINT32_TO_LE (table_offset);
|
*int32val = GUINT32_TO_LE (stream_desc [i].stream->index);
|
table_offset += GUINT32_FROM_LE (*int32val);
|
table_offset += 3; table_offset &= ~3;
|
p += 8;
|
strcpy ((char*)p, stream_desc [i].name);
|
p += strlen (stream_desc [i].name) + 1;
|
align_pointer (meta->raw_metadata, p);
|
}
|
/*
|
* now copy the data, the table stream header and contents goes first.
|
*/
|
g_assert ((p - (unsigned char*)meta->raw_metadata) < assembly->tstream.offset);
|
p = (guchar*)meta->raw_metadata + assembly->tstream.offset;
|
int32val = (guint32*)p;
|
*int32val = GUINT32_TO_LE (0); /* reserved */
|
p += 4;
|
|
*p++ = 2; /* version */
|
*p++ = 0;
|
|
if (meta->idx_string_wide)
|
*p |= 0x01;
|
if (meta->idx_guid_wide)
|
*p |= 0x02;
|
if (meta->idx_blob_wide)
|
*p |= 0x04;
|
++p;
|
*p++ = 1; /* reserved */
|
int64val = (guint64*)p;
|
*int64val++ = GUINT64_TO_LE (valid_mask);
|
*int64val++ = GUINT64_TO_LE (valid_mask & sorted_mask); /* bitvector of sorted tables */
|
p += 16;
|
int32val = (guint32*)p;
|
for (i = 0; i < MONO_TABLE_NUM; i++){
|
if (meta->tables [i].rows == 0)
|
continue;
|
*int32val++ = GUINT32_TO_LE (meta->tables [i].rows);
|
}
|
p = (unsigned char*)int32val;
|
|
/* sort the tables that still need sorting */
|
table = &assembly->tables [MONO_TABLE_CONSTANT];
|
if (table->rows)
|
qsort (table->values + MONO_CONSTANT_SIZE, table->rows, sizeof (guint32) * MONO_CONSTANT_SIZE, compare_constants);
|
table = &assembly->tables [MONO_TABLE_METHODSEMANTICS];
|
if (table->rows)
|
qsort (table->values + MONO_METHOD_SEMA_SIZE, table->rows, sizeof (guint32) * MONO_METHOD_SEMA_SIZE, compare_semantics);
|
table = &assembly->tables [MONO_TABLE_CUSTOMATTRIBUTE];
|
if (table->rows)
|
qsort (table->values + MONO_CUSTOM_ATTR_SIZE, table->rows, sizeof (guint32) * MONO_CUSTOM_ATTR_SIZE, compare_custom_attrs);
|
table = &assembly->tables [MONO_TABLE_FIELDMARSHAL];
|
if (table->rows)
|
qsort (table->values + MONO_FIELD_MARSHAL_SIZE, table->rows, sizeof (guint32) * MONO_FIELD_MARSHAL_SIZE, compare_field_marshal);
|
table = &assembly->tables [MONO_TABLE_NESTEDCLASS];
|
if (table->rows)
|
qsort (table->values + MONO_NESTED_CLASS_SIZE, table->rows, sizeof (guint32) * MONO_NESTED_CLASS_SIZE, compare_nested);
|
/* Section 21.11 DeclSecurity in Partition II doesn't specify this to be sorted by MS implementation requires it */
|
table = &assembly->tables [MONO_TABLE_DECLSECURITY];
|
if (table->rows)
|
qsort (table->values + MONO_DECL_SECURITY_SIZE, table->rows, sizeof (guint32) * MONO_DECL_SECURITY_SIZE, compare_declsecurity_attrs);
|
table = &assembly->tables [MONO_TABLE_INTERFACEIMPL];
|
if (table->rows)
|
qsort (table->values + MONO_INTERFACEIMPL_SIZE, table->rows, sizeof (guint32) * MONO_INTERFACEIMPL_SIZE, compare_interface_impl);
|
|
/* compress the tables */
|
for (i = 0; i < MONO_TABLE_NUM; i++){
|
int row, col;
|
guint32 *values;
|
guint32 bitfield = meta->tables [i].size_bitfield;
|
if (!meta->tables [i].rows)
|
continue;
|
if (assembly->tables [i].columns != mono_metadata_table_count (bitfield))
|
g_error ("col count mismatch in %d: %d %d", i, assembly->tables [i].columns, mono_metadata_table_count (bitfield));
|
meta->tables [i].base = (char*)p;
|
for (row = 1; row <= meta->tables [i].rows; ++row) {
|
values = assembly->tables [i].values + row * assembly->tables [i].columns;
|
for (col = 0; col < assembly->tables [i].columns; ++col) {
|
switch (mono_metadata_table_size (bitfield, col)) {
|
case 1:
|
*p++ = values [col];
|
break;
|
case 2:
|
*p++ = values [col] & 0xff;
|
*p++ = (values [col] >> 8) & 0xff;
|
break;
|
case 4:
|
*p++ = values [col] & 0xff;
|
*p++ = (values [col] >> 8) & 0xff;
|
*p++ = (values [col] >> 16) & 0xff;
|
*p++ = (values [col] >> 24) & 0xff;
|
break;
|
default:
|
g_assert_not_reached ();
|
}
|
}
|
}
|
g_assert ((p - (const unsigned char*)meta->tables [i].base) == (meta->tables [i].rows * meta->tables [i].row_size));
|
}
|
|
g_assert (assembly->guid.offset + assembly->guid.index < meta_size);
|
memcpy (meta->raw_metadata + assembly->sheap.offset, assembly->sheap.data, assembly->sheap.index);
|
memcpy (meta->raw_metadata + assembly->us.offset, assembly->us.data, assembly->us.index);
|
memcpy (meta->raw_metadata + assembly->blob.offset, assembly->blob.data, assembly->blob.index);
|
memcpy (meta->raw_metadata + assembly->guid.offset, assembly->guid.data, assembly->guid.index);
|
|
assembly->meta_size = assembly->guid.offset + assembly->guid.index;
|
|
return TRUE;
|
}
|
|
/*
|
* Some tables in metadata need to be sorted according to some criteria, but
|
* when methods and fields are first created with reflection, they may be assigned a token
|
* that doesn't correspond to the final token they will get assigned after the sorting.
|
* ILGenerator.cs keeps a fixup table that maps the position of tokens in the IL code stream
|
* with the reflection objects that represent them. Once all the tables are set up, the
|
* reflection objects will contains the correct table index. fixup_method() will fixup the
|
* tokens for the method with ILGenerator @ilgen.
|
*/
|
static void
|
fixup_method (MonoReflectionILGen *ilgen, gpointer value, MonoDynamicImage *assembly)
|
{
|
guint32 code_idx = GPOINTER_TO_UINT (value);
|
MonoReflectionILTokenInfo *iltoken;
|
MonoReflectionTypeBuilder *tb;
|
MonoReflectionArrayMethod *am;
|
guint32 i, idx = 0;
|
unsigned char *target;
|
|
for (i = 0; i < ilgen->num_token_fixups; ++i) {
|
iltoken = (MonoReflectionILTokenInfo *)mono_array_addr_with_size (ilgen->token_fixups, sizeof (MonoReflectionILTokenInfo), i);
|
target = (guchar*)assembly->code.data + code_idx + iltoken->code_pos;
|
switch (target [3]) {
|
case MONO_TABLE_FIELD:
|
if (!strcmp (iltoken->member->vtable->klass->name, "FieldBuilder")) {
|
g_assert_not_reached ();
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoField")) {
|
MonoClassField *f = ((MonoReflectionField*)iltoken->member)->field;
|
idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->field_to_table_idx, f));
|
} else {
|
g_assert_not_reached ();
|
}
|
break;
|
case MONO_TABLE_METHOD:
|
if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder")) {
|
g_assert_not_reached ();
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "ConstructorBuilder")) {
|
g_assert_not_reached ();
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod") ||
|
!strcmp (iltoken->member->vtable->klass->name, "MonoCMethod")) {
|
MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method;
|
idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, m));
|
} else {
|
g_assert_not_reached ();
|
}
|
break;
|
case MONO_TABLE_TYPEDEF:
|
if (!strcmp (iltoken->member->vtable->klass->name, "TypeBuilder")) {
|
g_assert_not_reached ();
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType")) {
|
MonoClass *k = mono_class_from_mono_type (((MonoReflectionType*)iltoken->member)->type);
|
MonoObject *obj = mono_class_get_ref_info_raw (k); /* FIXME use handles */
|
g_assert (obj);
|
g_assert (!strcmp (mono_object_class (obj)->name, "TypeBuilder"));
|
tb = (MonoReflectionTypeBuilder*)obj;
|
idx = tb->table_idx;
|
} else {
|
g_assert_not_reached ();
|
}
|
break;
|
case MONO_TABLE_TYPEREF:
|
g_assert (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType"));
|
MonoClass *k = mono_class_from_mono_type (((MonoReflectionType*)iltoken->member)->type);
|
MonoObject *obj = mono_class_get_ref_info_raw (k); /* FIXME use handles */
|
g_assert (obj);
|
g_assert (!strcmp (mono_object_class (obj)->name, "TypeBuilder"));
|
g_assert (((MonoReflectionTypeBuilder*)obj)->module->dynamic_image != assembly);
|
continue;
|
case MONO_TABLE_MEMBERREF:
|
if (!strcmp (iltoken->member->vtable->klass->name, "MonoArrayMethod")) {
|
am = (MonoReflectionArrayMethod*)iltoken->member;
|
idx = am->table_idx;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod") ||
|
!strcmp (iltoken->member->vtable->klass->name, "MonoCMethod")) {
|
MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method;
|
g_assert (mono_class_is_ginst (m->klass) || mono_class_is_gtd (m->klass));
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "FieldBuilder")) {
|
g_assert_not_reached ();
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MonoField")) {
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder") ||
|
!strcmp (iltoken->member->vtable->klass->name, "ConstructorBuilder")) {
|
g_assert_not_reached ();
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "FieldOnTypeBuilderInst")) {
|
g_assert_not_reached ();
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodOnTypeBuilderInst")) {
|
g_assert_not_reached ();
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "ConstructorOnTypeBuilderInst")) {
|
g_assert_not_reached ();
|
continue;
|
} else {
|
g_assert_not_reached ();
|
}
|
break;
|
case MONO_TABLE_METHODSPEC:
|
if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod")) {
|
MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method;
|
g_assert (mono_method_signature (m)->generic_param_count);
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodBuilder")) {
|
g_assert_not_reached ();
|
continue;
|
} else if (!strcmp (iltoken->member->vtable->klass->name, "MethodOnTypeBuilderInst")) {
|
g_assert_not_reached ();
|
continue;
|
} else {
|
g_assert_not_reached ();
|
}
|
break;
|
case MONO_TABLE_TYPESPEC:
|
if (!strcmp (iltoken->member->vtable->klass->name, "RuntimeType")) {
|
continue;
|
} else {
|
g_assert_not_reached ();
|
}
|
break;
|
default:
|
g_error ("got unexpected table 0x%02x in fixup", target [3]);
|
}
|
target [0] = idx & 0xff;
|
target [1] = (idx >> 8) & 0xff;
|
target [2] = (idx >> 16) & 0xff;
|
}
|
}
|
|
/*
|
* fixup_cattrs:
|
*
|
* The CUSTOM_ATTRIBUTE table might contain METHODDEF tokens whose final
|
* value is not known when the table is emitted.
|
*/
|
static void
|
fixup_cattrs (MonoDynamicImage *assembly)
|
{
|
MonoDynamicTable *table;
|
guint32 *values;
|
guint32 type, i, idx, token;
|
MonoObject *ctor;
|
|
table = &assembly->tables [MONO_TABLE_CUSTOMATTRIBUTE];
|
|
for (i = 0; i < table->rows; ++i) {
|
values = table->values + ((i + 1) * MONO_CUSTOM_ATTR_SIZE);
|
|
type = values [MONO_CUSTOM_ATTR_TYPE];
|
if ((type & MONO_CUSTOM_ATTR_TYPE_MASK) == MONO_CUSTOM_ATTR_TYPE_METHODDEF) {
|
idx = type >> MONO_CUSTOM_ATTR_TYPE_BITS;
|
token = mono_metadata_make_token (MONO_TABLE_METHOD, idx);
|
ctor = (MonoObject *)mono_g_hash_table_lookup (assembly->remapped_tokens, GUINT_TO_POINTER (token));
|
g_assert (ctor);
|
|
if (!strcmp (ctor->vtable->klass->name, "MonoCMethod")) {
|
MonoMethod *m = ((MonoReflectionMethod*)ctor)->method;
|
idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, m));
|
values [MONO_CUSTOM_ATTR_TYPE] = (idx << MONO_CUSTOM_ATTR_TYPE_BITS) | MONO_CUSTOM_ATTR_TYPE_METHODDEF;
|
} else if (!strcmp (ctor->vtable->klass->name, "ConstructorBuilder")) {
|
MonoMethod *m = ((MonoReflectionCtorBuilder*)ctor)->mhandle;
|
idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, m));
|
values [MONO_CUSTOM_ATTR_TYPE] = (idx << MONO_CUSTOM_ATTR_TYPE_BITS) | MONO_CUSTOM_ATTR_TYPE_METHODDEF;
|
}
|
}
|
}
|
}
|
|
static gboolean
|
assembly_add_resource_manifest (MonoReflectionModuleBuilder *mb, MonoDynamicImage *assembly, MonoReflectionResource *rsrc, guint32 implementation, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
guint32 *values;
|
|
error_init (error);
|
|
table = &assembly->tables [MONO_TABLE_MANIFESTRESOURCE];
|
table->rows++;
|
alloc_table (table, table->rows);
|
values = table->values + table->next_idx * MONO_MANIFEST_SIZE;
|
values [MONO_MANIFEST_OFFSET] = rsrc->offset;
|
values [MONO_MANIFEST_FLAGS] = rsrc->attrs;
|
values [MONO_MANIFEST_NAME] = string_heap_insert_mstring (&assembly->sheap, rsrc->name, error);
|
return_val_if_nok (error, FALSE);
|
values [MONO_MANIFEST_IMPLEMENTATION] = implementation;
|
table->next_idx++;
|
return TRUE;
|
}
|
|
static gboolean
|
assembly_add_resource (MonoReflectionModuleBuilder *mb, MonoDynamicImage *assembly, MonoReflectionResource *rsrc, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
guint32 *values;
|
char blob_size [6];
|
guchar hash [20];
|
char *b = blob_size;
|
char *name, *sname;
|
guint32 idx, offset;
|
|
error_init (error);
|
|
if (rsrc->filename) {
|
name = mono_string_to_utf8_checked (rsrc->filename, error);
|
return_val_if_nok (error, FALSE);
|
sname = g_path_get_basename (name);
|
|
table = &assembly->tables [MONO_TABLE_FILE];
|
table->rows++;
|
alloc_table (table, table->rows);
|
values = table->values + table->next_idx * MONO_FILE_SIZE;
|
values [MONO_FILE_FLAGS] = FILE_CONTAINS_NO_METADATA;
|
values [MONO_FILE_NAME] = string_heap_insert (&assembly->sheap, sname);
|
g_free (sname);
|
|
mono_sha1_get_digest_from_file (name, hash);
|
mono_metadata_encode_value (20, b, &b);
|
values [MONO_FILE_HASH_VALUE] = mono_image_add_stream_data (&assembly->blob, blob_size, b-blob_size);
|
mono_image_add_stream_data (&assembly->blob, (char*)hash, 20);
|
g_free (name);
|
idx = table->next_idx++;
|
rsrc->offset = 0;
|
idx = MONO_IMPLEMENTATION_FILE | (idx << MONO_IMPLEMENTATION_BITS);
|
} else {
|
char sizebuf [4];
|
char *data;
|
guint len;
|
if (rsrc->data) {
|
data = mono_array_addr (rsrc->data, char, 0);
|
len = mono_array_length (rsrc->data);
|
} else {
|
data = NULL;
|
len = 0;
|
}
|
offset = len;
|
sizebuf [0] = offset; sizebuf [1] = offset >> 8;
|
sizebuf [2] = offset >> 16; sizebuf [3] = offset >> 24;
|
rsrc->offset = mono_image_add_stream_data (&assembly->resources, sizebuf, 4);
|
mono_image_add_stream_data (&assembly->resources, data, len);
|
|
if (!mb->is_main)
|
/*
|
* The entry should be emitted into the MANIFESTRESOURCE table of
|
* the main module, but that needs to reference the FILE table
|
* which isn't emitted yet.
|
*/
|
return TRUE;
|
else
|
idx = 0;
|
}
|
|
return assembly_add_resource_manifest (mb, assembly, rsrc, idx, error);
|
}
|
|
static gboolean
|
set_version_from_string (MonoString *version, guint32 *values, MonoError *error)
|
{
|
gchar *ver, *p, *str;
|
guint32 i;
|
|
error_init (error);
|
|
values [MONO_ASSEMBLY_MAJOR_VERSION] = 0;
|
values [MONO_ASSEMBLY_MINOR_VERSION] = 0;
|
values [MONO_ASSEMBLY_REV_NUMBER] = 0;
|
values [MONO_ASSEMBLY_BUILD_NUMBER] = 0;
|
if (!version)
|
return TRUE;
|
ver = str = mono_string_to_utf8_checked (version, error);
|
return_val_if_nok (error, FALSE);
|
for (i = 0; i < 4; ++i) {
|
values [MONO_ASSEMBLY_MAJOR_VERSION + i] = strtol (ver, &p, 10);
|
switch (*p) {
|
case '.':
|
p++;
|
break;
|
case '*':
|
/* handle Revision and Build */
|
p++;
|
break;
|
}
|
ver = p;
|
}
|
g_free (str);
|
return TRUE;
|
}
|
|
static guint32
|
load_public_key (MonoArray *pkey, MonoDynamicImage *assembly) {
|
gsize len;
|
guint32 token = 0;
|
char blob_size [6];
|
char *b = blob_size;
|
|
if (!pkey)
|
return token;
|
|
len = mono_array_length (pkey);
|
mono_metadata_encode_value (len, b, &b);
|
token = mono_image_add_stream_data (&assembly->blob, blob_size, b - blob_size);
|
mono_image_add_stream_data (&assembly->blob, mono_array_addr (pkey, char, 0), len);
|
|
assembly->public_key = (guint8 *)g_malloc (len);
|
memcpy (assembly->public_key, mono_array_addr (pkey, char, 0), len);
|
assembly->public_key_len = len;
|
|
/* Special case: check for ECMA key (16 bytes) */
|
if ((len == MONO_ECMA_KEY_LENGTH) && mono_is_ecma_key (mono_array_addr (pkey, char, 0), len)) {
|
/* In this case we must reserve 128 bytes (1024 bits) for the signature */
|
assembly->strong_name_size = MONO_DEFAULT_PUBLIC_KEY_LENGTH;
|
} else if (len >= MONO_PUBLIC_KEY_HEADER_LENGTH + MONO_MINIMUM_PUBLIC_KEY_LENGTH) {
|
/* minimum key size (in 2.0) is 384 bits */
|
assembly->strong_name_size = len - MONO_PUBLIC_KEY_HEADER_LENGTH;
|
} else {
|
/* FIXME - verifier */
|
g_warning ("Invalid public key length: %d bits (total: %d)", (int)MONO_PUBLIC_KEY_BIT_SIZE (len), (int)len);
|
assembly->strong_name_size = MONO_DEFAULT_PUBLIC_KEY_LENGTH; /* to be safe */
|
}
|
assembly->strong_name = (char *)g_malloc0 (assembly->strong_name_size);
|
|
return token;
|
}
|
|
static gboolean
|
mono_image_emit_manifest (MonoReflectionModuleBuilder *moduleb, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
MonoDynamicImage *assembly;
|
MonoReflectionAssemblyBuilder *assemblyb;
|
MonoDomain *domain;
|
guint32 *values;
|
int i;
|
guint32 module_index;
|
|
error_init (error);
|
|
assemblyb = moduleb->assemblyb;
|
assembly = moduleb->dynamic_image;
|
domain = mono_object_domain (assemblyb);
|
|
/* Emit ASSEMBLY table */
|
table = &assembly->tables [MONO_TABLE_ASSEMBLY];
|
alloc_table (table, 1);
|
values = table->values + MONO_ASSEMBLY_SIZE;
|
values [MONO_ASSEMBLY_HASH_ALG] = assemblyb->algid? assemblyb->algid: ASSEMBLY_HASH_SHA1;
|
values [MONO_ASSEMBLY_NAME] = string_heap_insert_mstring (&assembly->sheap, assemblyb->name, error);
|
return_val_if_nok (error, FALSE);
|
if (assemblyb->culture) {
|
values [MONO_ASSEMBLY_CULTURE] = string_heap_insert_mstring (&assembly->sheap, assemblyb->culture, error);
|
return_val_if_nok (error, FALSE);
|
} else {
|
values [MONO_ASSEMBLY_CULTURE] = string_heap_insert (&assembly->sheap, "");
|
}
|
values [MONO_ASSEMBLY_PUBLIC_KEY] = load_public_key (assemblyb->public_key, assembly);
|
values [MONO_ASSEMBLY_FLAGS] = assemblyb->flags;
|
if (!set_version_from_string (assemblyb->version, values, error))
|
return FALSE;
|
|
/* Emit FILE + EXPORTED_TYPE table */
|
module_index = 0;
|
for (i = 0; i < mono_array_length (assemblyb->modules); ++i) {
|
int j;
|
MonoReflectionModuleBuilder *file_module =
|
mono_array_get (assemblyb->modules, MonoReflectionModuleBuilder*, i);
|
if (file_module != moduleb) {
|
if (!mono_image_fill_file_table (domain, (MonoReflectionModule*)file_module, assembly, error))
|
return FALSE;
|
module_index ++;
|
if (file_module->types) {
|
for (j = 0; j < file_module->num_types; ++j) {
|
MonoReflectionTypeBuilder *tb = mono_array_get (file_module->types, MonoReflectionTypeBuilder*, j);
|
mono_image_fill_export_table (domain, tb, module_index, 0, assembly, error);
|
return_val_if_nok (error, FALSE);
|
}
|
}
|
}
|
}
|
if (assemblyb->loaded_modules) {
|
for (i = 0; i < mono_array_length (assemblyb->loaded_modules); ++i) {
|
MonoReflectionModule *file_module =
|
mono_array_get (assemblyb->loaded_modules, MonoReflectionModule*, i);
|
if (!mono_image_fill_file_table (domain, file_module, assembly, error))
|
return FALSE;
|
module_index ++;
|
mono_image_fill_export_table_from_module (domain, file_module, module_index, assembly);
|
}
|
}
|
if (assemblyb->type_forwarders)
|
mono_image_fill_export_table_from_type_forwarders (assemblyb, assembly);
|
|
/* Emit MANIFESTRESOURCE table */
|
module_index = 0;
|
for (i = 0; i < mono_array_length (assemblyb->modules); ++i) {
|
int j;
|
MonoReflectionModuleBuilder *file_module =
|
mono_array_get (assemblyb->modules, MonoReflectionModuleBuilder*, i);
|
/* The table for the main module is emitted later */
|
if (file_module != moduleb) {
|
module_index ++;
|
if (file_module->resources) {
|
int len = mono_array_length (file_module->resources);
|
for (j = 0; j < len; ++j) {
|
MonoReflectionResource* res = (MonoReflectionResource*)mono_array_addr (file_module->resources, MonoReflectionResource, j);
|
if (!assembly_add_resource_manifest (file_module, assembly, res, MONO_IMPLEMENTATION_FILE | (module_index << MONO_IMPLEMENTATION_BITS), error))
|
return FALSE;
|
}
|
}
|
}
|
}
|
return TRUE;
|
}
|
|
#ifndef DISABLE_REFLECTION_EMIT_SAVE
|
|
/*
|
* Insert into the metadata tables all the info about the TypeBuilder tb.
|
* Data in the tables is inserted in a predefined order, since some tables need to be sorted.
|
*/
|
static gboolean
|
mono_image_get_type_info (MonoDomain *domain, MonoReflectionTypeBuilder *tb, MonoDynamicImage *assembly, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
guint *values;
|
int i, is_object = 0, is_system = 0;
|
char *n;
|
|
error_init (error);
|
|
table = &assembly->tables [MONO_TABLE_TYPEDEF];
|
values = table->values + tb->table_idx * MONO_TYPEDEF_SIZE;
|
values [MONO_TYPEDEF_FLAGS] = tb->attrs;
|
n = mono_string_to_utf8_checked (tb->name, error);
|
return_val_if_nok (error, FALSE);
|
if (strcmp (n, "Object") == 0)
|
is_object++;
|
values [MONO_TYPEDEF_NAME] = string_heap_insert (&assembly->sheap, n);
|
g_free (n);
|
n = mono_string_to_utf8_checked (tb->nspace, error);
|
return_val_if_nok (error, FALSE);
|
if (strcmp (n, "System") == 0)
|
is_system++;
|
values [MONO_TYPEDEF_NAMESPACE] = string_heap_insert (&assembly->sheap, n);
|
g_free (n);
|
if (tb->parent && !(is_system && is_object) &&
|
!(tb->attrs & TYPE_ATTRIBUTE_INTERFACE)) { /* interfaces don't have a parent */
|
MonoType *parent_type = mono_reflection_type_get_handle ((MonoReflectionType*)tb->parent, error);
|
return_val_if_nok (error, FALSE);
|
values [MONO_TYPEDEF_EXTENDS] = mono_image_typedef_or_ref (assembly, parent_type);
|
} else {
|
values [MONO_TYPEDEF_EXTENDS] = 0;
|
}
|
values [MONO_TYPEDEF_FIELD_LIST] = assembly->tables [MONO_TABLE_FIELD].next_idx;
|
values [MONO_TYPEDEF_METHOD_LIST] = assembly->tables [MONO_TABLE_METHOD].next_idx;
|
|
/*
|
* if we have explicitlayout or sequentiallayouts, output data in the
|
* ClassLayout table.
|
*/
|
if (((tb->attrs & TYPE_ATTRIBUTE_LAYOUT_MASK) != TYPE_ATTRIBUTE_AUTO_LAYOUT) &&
|
((tb->class_size > 0) || (tb->packing_size > 0))) {
|
table = &assembly->tables [MONO_TABLE_CLASSLAYOUT];
|
table->rows++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_CLASS_LAYOUT_SIZE;
|
values [MONO_CLASS_LAYOUT_PARENT] = tb->table_idx;
|
values [MONO_CLASS_LAYOUT_CLASS_SIZE] = tb->class_size;
|
values [MONO_CLASS_LAYOUT_PACKING_SIZE] = tb->packing_size;
|
}
|
|
/* handle interfaces */
|
if (tb->interfaces) {
|
table = &assembly->tables [MONO_TABLE_INTERFACEIMPL];
|
i = table->rows;
|
table->rows += mono_array_length (tb->interfaces);
|
alloc_table (table, table->rows);
|
values = table->values + (i + 1) * MONO_INTERFACEIMPL_SIZE;
|
for (i = 0; i < mono_array_length (tb->interfaces); ++i) {
|
MonoReflectionType* iface = (MonoReflectionType*) mono_array_get (tb->interfaces, gpointer, i);
|
MonoType *iface_type = mono_reflection_type_get_handle (iface, error);
|
return_val_if_nok (error, FALSE);
|
values [MONO_INTERFACEIMPL_CLASS] = tb->table_idx;
|
values [MONO_INTERFACEIMPL_INTERFACE] = mono_image_typedef_or_ref (assembly, iface_type);
|
values += MONO_INTERFACEIMPL_SIZE;
|
}
|
}
|
|
/* handle fields */
|
if (tb->fields) {
|
table = &assembly->tables [MONO_TABLE_FIELD];
|
table->rows += tb->num_fields;
|
alloc_table (table, table->rows);
|
for (i = 0; i < tb->num_fields; ++i) {
|
mono_image_get_field_info (
|
mono_array_get (tb->fields, MonoReflectionFieldBuilder*, i), assembly, error);
|
return_val_if_nok (error, FALSE);
|
}
|
}
|
|
/* handle constructors */
|
if (tb->ctors) {
|
table = &assembly->tables [MONO_TABLE_METHOD];
|
table->rows += mono_array_length (tb->ctors);
|
alloc_table (table, table->rows);
|
for (i = 0; i < mono_array_length (tb->ctors); ++i) {
|
if (!mono_image_get_ctor_info (domain,
|
mono_array_get (tb->ctors, MonoReflectionCtorBuilder*, i),
|
assembly, error))
|
return FALSE;
|
}
|
}
|
|
/* handle methods */
|
if (tb->methods) {
|
table = &assembly->tables [MONO_TABLE_METHOD];
|
table->rows += tb->num_methods;
|
alloc_table (table, table->rows);
|
for (i = 0; i < tb->num_methods; ++i) {
|
if (!mono_image_get_method_info (
|
mono_array_get (tb->methods, MonoReflectionMethodBuilder*, i), assembly, error))
|
return FALSE;
|
}
|
}
|
|
/* Do the same with properties etc.. */
|
if (tb->events && mono_array_length (tb->events)) {
|
table = &assembly->tables [MONO_TABLE_EVENT];
|
table->rows += mono_array_length (tb->events);
|
alloc_table (table, table->rows);
|
table = &assembly->tables [MONO_TABLE_EVENTMAP];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_EVENT_MAP_SIZE;
|
values [MONO_EVENT_MAP_PARENT] = tb->table_idx;
|
values [MONO_EVENT_MAP_EVENTLIST] = assembly->tables [MONO_TABLE_EVENT].next_idx;
|
for (i = 0; i < mono_array_length (tb->events); ++i) {
|
mono_image_get_event_info (
|
mono_array_get (tb->events, MonoReflectionEventBuilder*, i), assembly, error);
|
return_val_if_nok (error, FALSE);
|
}
|
}
|
if (tb->properties && mono_array_length (tb->properties)) {
|
table = &assembly->tables [MONO_TABLE_PROPERTY];
|
table->rows += mono_array_length (tb->properties);
|
alloc_table (table, table->rows);
|
table = &assembly->tables [MONO_TABLE_PROPERTYMAP];
|
table->rows ++;
|
alloc_table (table, table->rows);
|
values = table->values + table->rows * MONO_PROPERTY_MAP_SIZE;
|
values [MONO_PROPERTY_MAP_PARENT] = tb->table_idx;
|
values [MONO_PROPERTY_MAP_PROPERTY_LIST] = assembly->tables [MONO_TABLE_PROPERTY].next_idx;
|
for (i = 0; i < mono_array_length (tb->properties); ++i) {
|
mono_image_get_property_info (
|
mono_array_get (tb->properties, MonoReflectionPropertyBuilder*, i), assembly, error);
|
return_val_if_nok (error, FALSE);
|
}
|
}
|
|
/* handle generic parameters */
|
if (tb->generic_params) {
|
table = &assembly->tables [MONO_TABLE_GENERICPARAM];
|
table->rows += mono_array_length (tb->generic_params);
|
alloc_table (table, table->rows);
|
for (i = 0; i < mono_array_length (tb->generic_params); ++i) {
|
guint32 owner = MONO_TYPEORMETHOD_TYPE | (tb->table_idx << MONO_TYPEORMETHOD_BITS);
|
|
mono_image_get_generic_param_info (
|
mono_array_get (tb->generic_params, MonoReflectionGenericParam*, i), owner, assembly);
|
}
|
}
|
|
mono_image_add_decl_security (assembly,
|
mono_metadata_make_token (MONO_TABLE_TYPEDEF, tb->table_idx), tb->permissions);
|
|
if (tb->subtypes) {
|
MonoDynamicTable *ntable;
|
|
ntable = &assembly->tables [MONO_TABLE_NESTEDCLASS];
|
ntable->rows += mono_array_length (tb->subtypes);
|
alloc_table (ntable, ntable->rows);
|
values = ntable->values + ntable->next_idx * MONO_NESTED_CLASS_SIZE;
|
|
for (i = 0; i < mono_array_length (tb->subtypes); ++i) {
|
MonoReflectionTypeBuilder *subtype = mono_array_get (tb->subtypes, MonoReflectionTypeBuilder*, i);
|
|
values [MONO_NESTED_CLASS_NESTED] = subtype->table_idx;
|
values [MONO_NESTED_CLASS_ENCLOSING] = tb->table_idx;
|
/*g_print ("nesting %s (%d) in %s (%d) (rows %d/%d)\n",
|
mono_string_to_utf8 (subtype->name), subtype->table_idx,
|
mono_string_to_utf8 (tb->name), tb->table_idx,
|
ntable->next_idx, ntable->rows);*/
|
values += MONO_NESTED_CLASS_SIZE;
|
ntable->next_idx++;
|
}
|
}
|
|
return TRUE;
|
}
|
|
|
/*
|
* mono_image_build_metadata() will fill the info in all the needed metadata tables
|
* for the modulebuilder @moduleb.
|
* At the end of the process, method and field tokens are fixed up and the
|
* on-disk compressed metadata representation is created.
|
* Return TRUE on success, or FALSE on failure and sets @error
|
*/
|
gboolean
|
mono_image_build_metadata (MonoReflectionModuleBuilder *moduleb, MonoError *error)
|
{
|
MonoDynamicTable *table;
|
MonoDynamicImage *assembly;
|
MonoReflectionAssemblyBuilder *assemblyb;
|
MonoDomain *domain;
|
MonoPtrArray types;
|
guint32 *values;
|
int i, j;
|
|
error_init (error);
|
|
assemblyb = moduleb->assemblyb;
|
assembly = moduleb->dynamic_image;
|
domain = mono_object_domain (assemblyb);
|
|
if (assembly->text_rva)
|
return TRUE;
|
|
assembly->text_rva = START_TEXT_RVA;
|
|
if (moduleb->is_main) {
|
mono_image_emit_manifest (moduleb, error);
|
return_val_if_nok (error, FALSE);
|
}
|
|
table = &assembly->tables [MONO_TABLE_TYPEDEF];
|
table->rows = 1; /* .<Module> */
|
table->next_idx++;
|
alloc_table (table, table->rows);
|
/*
|
* Set the first entry.
|
*/
|
values = table->values + table->columns;
|
values [MONO_TYPEDEF_FLAGS] = 0;
|
values [MONO_TYPEDEF_NAME] = string_heap_insert (&assembly->sheap, "<Module>") ;
|
values [MONO_TYPEDEF_NAMESPACE] = string_heap_insert (&assembly->sheap, "") ;
|
values [MONO_TYPEDEF_EXTENDS] = 0;
|
values [MONO_TYPEDEF_FIELD_LIST] = 1;
|
values [MONO_TYPEDEF_METHOD_LIST] = 1;
|
|
/*
|
* handle global methods
|
* FIXME: test what to do when global methods are defined in multiple modules.
|
*/
|
if (moduleb->global_methods) {
|
table = &assembly->tables [MONO_TABLE_METHOD];
|
table->rows += mono_array_length (moduleb->global_methods);
|
alloc_table (table, table->rows);
|
for (i = 0; i < mono_array_length (moduleb->global_methods); ++i) {
|
if (!mono_image_get_method_info (
|
mono_array_get (moduleb->global_methods, MonoReflectionMethodBuilder*, i), assembly, error))
|
goto leave;
|
}
|
}
|
if (moduleb->global_fields) {
|
table = &assembly->tables [MONO_TABLE_FIELD];
|
table->rows += mono_array_length (moduleb->global_fields);
|
alloc_table (table, table->rows);
|
for (i = 0; i < mono_array_length (moduleb->global_fields); ++i) {
|
mono_image_get_field_info (
|
mono_array_get (moduleb->global_fields, MonoReflectionFieldBuilder*, i), assembly,
|
error);
|
goto_if_nok (error, leave);
|
}
|
}
|
|
table = &assembly->tables [MONO_TABLE_MODULE];
|
alloc_table (table, 1);
|
mono_image_fill_module_table (domain, moduleb, assembly, error);
|
goto_if_nok (error, leave);
|
|
/* Collect all types into a list sorted by their table_idx */
|
mono_ptr_array_init (types, moduleb->num_types, MONO_ROOT_SOURCE_REFLECTION, NULL, "Reflection Dynamic Image Type List");
|
|
if (moduleb->types)
|
for (i = 0; i < moduleb->num_types; ++i) {
|
MonoReflectionTypeBuilder *type = mono_array_get (moduleb->types, MonoReflectionTypeBuilder*, i);
|
collect_types (&types, type);
|
}
|
|
mono_ptr_array_sort (types, (int (*)(const void *, const void *))compare_types_by_table_idx);
|
table = &assembly->tables [MONO_TABLE_TYPEDEF];
|
table->rows += mono_ptr_array_size (types);
|
alloc_table (table, table->rows);
|
|
/*
|
* Emit type names + namespaces at one place inside the string heap,
|
* so load_class_names () needs to touch fewer pages.
|
*/
|
for (i = 0; i < mono_ptr_array_size (types); ++i) {
|
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i);
|
string_heap_insert_mstring (&assembly->sheap, tb->nspace, error);
|
goto_if_nok (error, leave_types);
|
}
|
for (i = 0; i < mono_ptr_array_size (types); ++i) {
|
MonoReflectionTypeBuilder *tb = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i);
|
string_heap_insert_mstring (&assembly->sheap, tb->name, error);
|
goto_if_nok (error, leave_types);
|
}
|
|
for (i = 0; i < mono_ptr_array_size (types); ++i) {
|
MonoReflectionTypeBuilder *type = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i);
|
if (!mono_image_get_type_info (domain, type, assembly, error))
|
goto leave_types;
|
}
|
|
/*
|
* table->rows is already set above and in mono_image_fill_module_table.
|
*/
|
/* add all the custom attributes at the end, once all the indexes are stable */
|
if (!mono_image_add_cattrs (assembly, 1, MONO_CUSTOM_ATTR_ASSEMBLY, assemblyb->cattrs, error))
|
goto leave_types;
|
|
/* CAS assembly permissions */
|
if (assemblyb->permissions_minimum)
|
mono_image_add_decl_security (assembly, mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1), assemblyb->permissions_minimum);
|
if (assemblyb->permissions_optional)
|
mono_image_add_decl_security (assembly, mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1), assemblyb->permissions_optional);
|
if (assemblyb->permissions_refused)
|
mono_image_add_decl_security (assembly, mono_metadata_make_token (MONO_TABLE_ASSEMBLY, 1), assemblyb->permissions_refused);
|
|
if (!module_add_cattrs (assembly, moduleb, error))
|
goto leave_types;
|
|
/* fixup tokens */
|
mono_g_hash_table_foreach (assembly->token_fixups, (GHFunc)fixup_method, assembly);
|
|
/* Create the MethodImpl table. We do this after emitting all methods so we already know
|
* the final tokens and don't need another fixup pass. */
|
|
if (moduleb->global_methods) {
|
for (i = 0; i < mono_array_length (moduleb->global_methods); ++i) {
|
MonoReflectionMethodBuilder *mb = mono_array_get (
|
moduleb->global_methods, MonoReflectionMethodBuilder*, i);
|
if (!mono_image_add_methodimpl (assembly, mb, error))
|
goto leave_types;
|
}
|
}
|
|
for (i = 0; i < mono_ptr_array_size (types); ++i) {
|
MonoReflectionTypeBuilder *type = (MonoReflectionTypeBuilder *)mono_ptr_array_get (types, i);
|
if (type->methods) {
|
for (j = 0; j < type->num_methods; ++j) {
|
MonoReflectionMethodBuilder *mb = mono_array_get (
|
type->methods, MonoReflectionMethodBuilder*, j);
|
|
if (!mono_image_add_methodimpl (assembly, mb, error))
|
goto leave_types;
|
}
|
}
|
}
|
|
fixup_cattrs (assembly);
|
|
leave_types:
|
mono_ptr_array_destroy (types);
|
leave:
|
|
return mono_error_ok (error);
|
}
|
|
#else /* DISABLE_REFLECTION_EMIT_SAVE */
|
|
gboolean
|
mono_image_build_metadata (MonoReflectionModuleBuilder *moduleb, MonoError *error)
|
{
|
g_error ("This mono runtime was configured with --enable-minimal=reflection_emit_save, so saving of dynamic assemblies is not supported.");
|
}
|
|
#endif /* DISABLE_REFLECTION_EMIT_SAVE */
|
|
#ifndef DISABLE_REFLECTION_EMIT_SAVE
|
|
static int
|
calc_section_size (MonoDynamicImage *assembly)
|
{
|
int nsections = 0;
|
|
/* alignment constraints */
|
mono_image_add_stream_zero (&assembly->code, 4 - (assembly->code.index % 4));
|
g_assert ((assembly->code.index % 4) == 0);
|
assembly->meta_size += 3;
|
assembly->meta_size &= ~3;
|
mono_image_add_stream_zero (&assembly->resources, 4 - (assembly->resources.index % 4));
|
g_assert ((assembly->resources.index % 4) == 0);
|
|
assembly->sections [MONO_SECTION_TEXT].size = assembly->meta_size + assembly->code.index + assembly->resources.index + assembly->strong_name_size;
|
assembly->sections [MONO_SECTION_TEXT].attrs = SECT_FLAGS_HAS_CODE | SECT_FLAGS_MEM_EXECUTE | SECT_FLAGS_MEM_READ;
|
nsections++;
|
|
if (assembly->win32_res) {
|
guint32 res_size = (assembly->win32_res_size + 3) & ~3;
|
|
assembly->sections [MONO_SECTION_RSRC].size = res_size;
|
assembly->sections [MONO_SECTION_RSRC].attrs = SECT_FLAGS_HAS_INITIALIZED_DATA | SECT_FLAGS_MEM_READ;
|
nsections++;
|
}
|
|
assembly->sections [MONO_SECTION_RELOC].size = 12;
|
assembly->sections [MONO_SECTION_RELOC].attrs = SECT_FLAGS_MEM_READ | SECT_FLAGS_MEM_DISCARDABLE | SECT_FLAGS_HAS_INITIALIZED_DATA;
|
nsections++;
|
|
return nsections;
|
}
|
|
typedef struct {
|
guint32 id;
|
guint32 offset;
|
GSList *children;
|
MonoReflectionWin32Resource *win32_res; /* Only for leaf nodes */
|
} ResTreeNode;
|
|
static int
|
resource_tree_compare_by_id (gconstpointer a, gconstpointer b)
|
{
|
ResTreeNode *t1 = (ResTreeNode*)a;
|
ResTreeNode *t2 = (ResTreeNode*)b;
|
|
return t1->id - t2->id;
|
}
|
|
/*
|
* resource_tree_create:
|
*
|
* Organize the resources into a resource tree.
|
*/
|
static ResTreeNode *
|
resource_tree_create (MonoArray *win32_resources)
|
{
|
ResTreeNode *tree, *res_node, *type_node, *lang_node;
|
GSList *l;
|
int i;
|
|
tree = g_new0 (ResTreeNode, 1);
|
|
for (i = 0; i < mono_array_length (win32_resources); ++i) {
|
MonoReflectionWin32Resource *win32_res =
|
(MonoReflectionWin32Resource*)mono_array_addr (win32_resources, MonoReflectionWin32Resource, i);
|
|
/* Create node */
|
|
/* FIXME: BUG: this stores managed references in unmanaged memory */
|
lang_node = g_new0 (ResTreeNode, 1);
|
lang_node->id = win32_res->lang_id;
|
lang_node->win32_res = win32_res;
|
|
/* Create type node if neccesary */
|
type_node = NULL;
|
for (l = tree->children; l; l = l->next)
|
if (((ResTreeNode*)(l->data))->id == win32_res->res_type) {
|
type_node = (ResTreeNode*)l->data;
|
break;
|
}
|
|
if (!type_node) {
|
type_node = g_new0 (ResTreeNode, 1);
|
type_node->id = win32_res->res_type;
|
|
/*
|
* The resource types have to be sorted otherwise
|
* Windows Explorer can't display the version information.
|
*/
|
tree->children = g_slist_insert_sorted (tree->children,
|
type_node, resource_tree_compare_by_id);
|
}
|
|
/* Create res node if neccesary */
|
res_node = NULL;
|
for (l = type_node->children; l; l = l->next)
|
if (((ResTreeNode*)(l->data))->id == win32_res->res_id) {
|
res_node = (ResTreeNode*)l->data;
|
break;
|
}
|
|
if (!res_node) {
|
res_node = g_new0 (ResTreeNode, 1);
|
res_node->id = win32_res->res_id;
|
type_node->children = g_slist_append (type_node->children, res_node);
|
}
|
|
res_node->children = g_slist_append (res_node->children, lang_node);
|
}
|
|
return tree;
|
}
|
|
/*
|
* resource_tree_encode:
|
*
|
* Encode the resource tree into the format used in the PE file.
|
*/
|
static void
|
resource_tree_encode (ResTreeNode *node, char *begin, char *p, char **endbuf)
|
{
|
char *entries;
|
MonoPEResourceDir dir;
|
MonoPEResourceDirEntry dir_entry;
|
MonoPEResourceDataEntry data_entry;
|
GSList *l;
|
guint32 res_id_entries;
|
|
/*
|
* For the format of the resource directory, see the article
|
* "An In-Depth Look into the Win32 Portable Executable File Format" by
|
* Matt Pietrek
|
*/
|
|
memset (&dir, 0, sizeof (dir));
|
memset (&dir_entry, 0, sizeof (dir_entry));
|
memset (&data_entry, 0, sizeof (data_entry));
|
|
g_assert (sizeof (dir) == 16);
|
g_assert (sizeof (dir_entry) == 8);
|
g_assert (sizeof (data_entry) == 16);
|
|
node->offset = p - begin;
|
|
/* IMAGE_RESOURCE_DIRECTORY */
|
res_id_entries = g_slist_length (node->children);
|
dir.res_id_entries = GUINT16_TO_LE (res_id_entries);
|
|
memcpy (p, &dir, sizeof (dir));
|
p += sizeof (dir);
|
|
/* Reserve space for entries */
|
entries = p;
|
p += sizeof (dir_entry) * res_id_entries;
|
|
/* Write children */
|
for (l = node->children; l; l = l->next) {
|
ResTreeNode *child = (ResTreeNode*)l->data;
|
|
if (child->win32_res) {
|
guint32 size;
|
|
child->offset = p - begin;
|
|
/* IMAGE_RESOURCE_DATA_ENTRY */
|
data_entry.rde_data_offset = GUINT32_TO_LE (p - begin + sizeof (data_entry));
|
size = mono_array_length (child->win32_res->res_data);
|
data_entry.rde_size = GUINT32_TO_LE (size);
|
|
memcpy (p, &data_entry, sizeof (data_entry));
|
p += sizeof (data_entry);
|
|
memcpy (p, mono_array_addr (child->win32_res->res_data, char, 0), size);
|
p += size;
|
} else {
|
resource_tree_encode (child, begin, p, &p);
|
}
|
}
|
|
/* IMAGE_RESOURCE_ENTRY */
|
for (l = node->children; l; l = l->next) {
|
ResTreeNode *child = (ResTreeNode*)l->data;
|
|
MONO_PE_RES_DIR_ENTRY_SET_NAME (dir_entry, FALSE, child->id);
|
MONO_PE_RES_DIR_ENTRY_SET_DIR (dir_entry, !child->win32_res, child->offset);
|
|
memcpy (entries, &dir_entry, sizeof (dir_entry));
|
entries += sizeof (dir_entry);
|
}
|
|
*endbuf = p;
|
}
|
|
static void
|
resource_tree_free (ResTreeNode * node)
|
{
|
GSList * list;
|
for (list = node->children; list; list = list->next)
|
resource_tree_free ((ResTreeNode*)list->data);
|
g_slist_free(node->children);
|
g_free (node);
|
}
|
|
static void
|
assembly_add_win32_resources (MonoDynamicImage *assembly, MonoReflectionAssemblyBuilder *assemblyb)
|
{
|
char *buf;
|
char *p;
|
guint32 size, i;
|
MonoReflectionWin32Resource *win32_res;
|
ResTreeNode *tree;
|
|
if (!assemblyb->win32_resources)
|
return;
|
|
/*
|
* Resources are stored in a three level tree inside the PE file.
|
* - level one contains a node for each type of resource
|
* - level two contains a node for each resource
|
* - level three contains a node for each instance of a resource for a
|
* specific language.
|
*/
|
|
tree = resource_tree_create (assemblyb->win32_resources);
|
|
/* Estimate the size of the encoded tree */
|
size = 0;
|
for (i = 0; i < mono_array_length (assemblyb->win32_resources); ++i) {
|
win32_res = (MonoReflectionWin32Resource*)mono_array_addr (assemblyb->win32_resources, MonoReflectionWin32Resource, i);
|
size += mono_array_length (win32_res->res_data);
|
}
|
/* Directory structure */
|
size += mono_array_length (assemblyb->win32_resources) * 256;
|
p = buf = (char *)g_malloc (size);
|
|
resource_tree_encode (tree, p, p, &p);
|
|
g_assert (p - buf <= size);
|
|
assembly->win32_res = (char *)g_malloc (p - buf);
|
assembly->win32_res_size = p - buf;
|
memcpy (assembly->win32_res, buf, p - buf);
|
|
g_free (buf);
|
resource_tree_free (tree);
|
}
|
|
static void
|
fixup_resource_directory (char *res_section, char *p, guint32 rva)
|
{
|
MonoPEResourceDir *dir = (MonoPEResourceDir*)p;
|
int i;
|
|
p += sizeof (MonoPEResourceDir);
|
for (i = 0; i < GUINT16_FROM_LE (dir->res_named_entries) + GUINT16_FROM_LE (dir->res_id_entries); ++i) {
|
MonoPEResourceDirEntry *dir_entry = (MonoPEResourceDirEntry*)p;
|
char *child = res_section + MONO_PE_RES_DIR_ENTRY_DIR_OFFSET (*dir_entry);
|
if (MONO_PE_RES_DIR_ENTRY_IS_DIR (*dir_entry)) {
|
fixup_resource_directory (res_section, child, rva);
|
} else {
|
MonoPEResourceDataEntry *data_entry = (MonoPEResourceDataEntry*)child;
|
data_entry->rde_data_offset = GUINT32_TO_LE (GUINT32_FROM_LE (data_entry->rde_data_offset) + rva);
|
}
|
|
p += sizeof (MonoPEResourceDirEntry);
|
}
|
}
|
|
static void
|
checked_write_file (HANDLE f, gconstpointer buffer, guint32 numbytes)
|
{
|
guint32 dummy;
|
if (!mono_w32file_write (f, buffer, numbytes, &dummy))
|
g_error ("mono_w32file_write returned %d\n", mono_w32error_get_last ());
|
}
|
|
/*
|
* mono_image_create_pefile:
|
* @mb: a module builder object
|
*
|
* This function creates the PE-COFF header, the image sections, the CLI header * etc. all the data is written in
|
* assembly->pefile where it can be easily retrieved later in chunks.
|
*/
|
gboolean
|
mono_image_create_pefile (MonoReflectionModuleBuilder *mb, HANDLE file, MonoError *error)
|
{
|
MonoMSDOSHeader *msdos;
|
MonoDotNetHeader *header;
|
MonoSectionTable *section;
|
MonoCLIHeader *cli_header;
|
guint32 size, image_size, virtual_base, text_offset;
|
guint32 header_start, section_start, file_offset, virtual_offset;
|
MonoDynamicImage *assembly;
|
MonoReflectionAssemblyBuilder *assemblyb;
|
MonoDynamicStream pefile_stream = {0};
|
MonoDynamicStream *pefile = &pefile_stream;
|
int i, nsections;
|
guint32 *rva, value;
|
guchar *p;
|
static const unsigned char msheader[] = {
|
0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
|
0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
|
0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68,
|
0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f,
|
0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,
|
0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
};
|
|
error_init (error);
|
|
assemblyb = mb->assemblyb;
|
|
mono_reflection_dynimage_basic_init (assemblyb);
|
assembly = mb->dynamic_image;
|
|
assembly->pe_kind = assemblyb->pe_kind;
|
assembly->machine = assemblyb->machine;
|
((MonoDynamicImage*)assemblyb->dynamic_assembly->assembly.image)->pe_kind = assemblyb->pe_kind;
|
((MonoDynamicImage*)assemblyb->dynamic_assembly->assembly.image)->machine = assemblyb->machine;
|
|
if (!mono_image_build_metadata (mb, error))
|
return FALSE;
|
|
|
if (mb->is_main && assemblyb->resources) {
|
int len = mono_array_length (assemblyb->resources);
|
for (i = 0; i < len; ++i) {
|
if (!assembly_add_resource (mb, assembly, (MonoReflectionResource*)mono_array_addr (assemblyb->resources, MonoReflectionResource, i), error))
|
return FALSE;
|
}
|
}
|
|
if (mb->resources) {
|
int len = mono_array_length (mb->resources);
|
for (i = 0; i < len; ++i) {
|
if (!assembly_add_resource (mb, assembly, (MonoReflectionResource*)mono_array_addr (mb->resources, MonoReflectionResource, i), error))
|
return FALSE;
|
}
|
}
|
|
if (!build_compressed_metadata (assembly, error))
|
return FALSE;
|
|
if (mb->is_main)
|
assembly_add_win32_resources (assembly, assemblyb);
|
|
nsections = calc_section_size (assembly);
|
|
/* The DOS header and stub */
|
g_assert (sizeof (MonoMSDOSHeader) == sizeof (msheader));
|
mono_image_add_stream_data (pefile, (char*)msheader, sizeof (msheader));
|
|
/* the dotnet header */
|
header_start = mono_image_add_stream_zero (pefile, sizeof (MonoDotNetHeader));
|
|
/* the section tables */
|
section_start = mono_image_add_stream_zero (pefile, sizeof (MonoSectionTable) * nsections);
|
|
file_offset = section_start + sizeof (MonoSectionTable) * nsections;
|
virtual_offset = VIRT_ALIGN;
|
image_size = 0;
|
|
for (i = 0; i < MONO_SECTION_MAX; ++i) {
|
if (!assembly->sections [i].size)
|
continue;
|
/* align offsets */
|
file_offset += FILE_ALIGN - 1;
|
file_offset &= ~(FILE_ALIGN - 1);
|
virtual_offset += VIRT_ALIGN - 1;
|
virtual_offset &= ~(VIRT_ALIGN - 1);
|
|
assembly->sections [i].offset = file_offset;
|
assembly->sections [i].rva = virtual_offset;
|
|
file_offset += assembly->sections [i].size;
|
virtual_offset += assembly->sections [i].size;
|
image_size += (assembly->sections [i].size + VIRT_ALIGN - 1) & ~(VIRT_ALIGN - 1);
|
}
|
|
file_offset += FILE_ALIGN - 1;
|
file_offset &= ~(FILE_ALIGN - 1);
|
|
image_size += section_start + sizeof (MonoSectionTable) * nsections;
|
|
/* back-patch info */
|
msdos = (MonoMSDOSHeader*)pefile->data;
|
msdos->pe_offset = GUINT32_FROM_LE (sizeof (MonoMSDOSHeader));
|
|
header = (MonoDotNetHeader*)(pefile->data + header_start);
|
header->pesig [0] = 'P';
|
header->pesig [1] = 'E';
|
|
header->coff.coff_machine = GUINT16_FROM_LE (assemblyb->machine);
|
header->coff.coff_sections = GUINT16_FROM_LE (nsections);
|
header->coff.coff_time = GUINT32_FROM_LE (time (NULL));
|
header->coff.coff_opt_header_size = GUINT16_FROM_LE (sizeof (MonoDotNetHeader) - sizeof (MonoCOFFHeader) - 4);
|
if (assemblyb->pekind == 1) {
|
/* it's a dll */
|
header->coff.coff_attributes = GUINT16_FROM_LE (0x210e);
|
} else {
|
/* it's an exe */
|
header->coff.coff_attributes = GUINT16_FROM_LE (0x010e);
|
}
|
|
virtual_base = 0x400000; /* FIXME: 0x10000000 if a DLL */
|
|
header->pe.pe_magic = GUINT16_FROM_LE (0x10B);
|
header->pe.pe_major = 6;
|
header->pe.pe_minor = 0;
|
size = assembly->sections [MONO_SECTION_TEXT].size;
|
size += FILE_ALIGN - 1;
|
size &= ~(FILE_ALIGN - 1);
|
header->pe.pe_code_size = GUINT32_FROM_LE(size);
|
size = assembly->sections [MONO_SECTION_RSRC].size;
|
size += FILE_ALIGN - 1;
|
size &= ~(FILE_ALIGN - 1);
|
header->pe.pe_data_size = GUINT32_FROM_LE(size);
|
g_assert (START_TEXT_RVA == assembly->sections [MONO_SECTION_TEXT].rva);
|
header->pe.pe_rva_code_base = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_TEXT].rva);
|
header->pe.pe_rva_data_base = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RSRC].rva);
|
/* pe_rva_entry_point always at the beginning of the text section */
|
header->pe.pe_rva_entry_point = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_TEXT].rva);
|
|
header->nt.pe_image_base = GUINT32_FROM_LE (virtual_base);
|
header->nt.pe_section_align = GUINT32_FROM_LE (VIRT_ALIGN);
|
header->nt.pe_file_alignment = GUINT32_FROM_LE (FILE_ALIGN);
|
header->nt.pe_os_major = GUINT16_FROM_LE (4);
|
header->nt.pe_os_minor = GUINT16_FROM_LE (0);
|
header->nt.pe_subsys_major = GUINT16_FROM_LE (4);
|
size = section_start;
|
size += FILE_ALIGN - 1;
|
size &= ~(FILE_ALIGN - 1);
|
header->nt.pe_header_size = GUINT32_FROM_LE (size);
|
size = image_size;
|
size += VIRT_ALIGN - 1;
|
size &= ~(VIRT_ALIGN - 1);
|
header->nt.pe_image_size = GUINT32_FROM_LE (size);
|
|
/*
|
// Translate the PEFileKind value to the value expected by the Windows loader
|
*/
|
{
|
short kind;
|
|
/*
|
// PEFileKinds.Dll == 1
|
// PEFileKinds.ConsoleApplication == 2
|
// PEFileKinds.WindowApplication == 3
|
//
|
// need to get:
|
// IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
|
// IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
|
*/
|
if (assemblyb->pekind == 3)
|
kind = 2;
|
else
|
kind = 3;
|
|
header->nt.pe_subsys_required = GUINT16_FROM_LE (kind);
|
}
|
header->nt.pe_stack_reserve = GUINT32_FROM_LE (0x00100000);
|
header->nt.pe_stack_commit = GUINT32_FROM_LE (0x00001000);
|
header->nt.pe_heap_reserve = GUINT32_FROM_LE (0x00100000);
|
header->nt.pe_heap_commit = GUINT32_FROM_LE (0x00001000);
|
header->nt.pe_loader_flags = GUINT32_FROM_LE (0);
|
header->nt.pe_data_dir_count = GUINT32_FROM_LE (16);
|
|
/* fill data directory entries */
|
|
header->datadir.pe_resource_table.size = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RSRC].size);
|
header->datadir.pe_resource_table.rva = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RSRC].rva);
|
|
header->datadir.pe_reloc_table.size = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RELOC].size);
|
header->datadir.pe_reloc_table.rva = GUINT32_FROM_LE (assembly->sections [MONO_SECTION_RELOC].rva);
|
|
header->datadir.pe_cli_header.size = GUINT32_FROM_LE (72);
|
header->datadir.pe_cli_header.rva = GUINT32_FROM_LE (assembly->text_rva + assembly->cli_header_offset);
|
header->datadir.pe_iat.size = GUINT32_FROM_LE (8);
|
header->datadir.pe_iat.rva = GUINT32_FROM_LE (assembly->text_rva + assembly->iat_offset);
|
/* patch entrypoint name */
|
if (assemblyb->pekind == 1)
|
memcpy (assembly->code.data + assembly->imp_names_offset + 2, "_CorDllMain", 12);
|
else
|
memcpy (assembly->code.data + assembly->imp_names_offset + 2, "_CorExeMain", 12);
|
/* patch imported function RVA name */
|
rva = (guint32*)(assembly->code.data + assembly->iat_offset);
|
*rva = GUINT32_FROM_LE (assembly->text_rva + assembly->imp_names_offset);
|
|
/* the import table */
|
header->datadir.pe_import_table.size = GUINT32_FROM_LE (79); /* FIXME: magic number? */
|
header->datadir.pe_import_table.rva = GUINT32_FROM_LE (assembly->text_rva + assembly->idt_offset);
|
/* patch imported dll RVA name and other entries in the dir */
|
rva = (guint32*)(assembly->code.data + assembly->idt_offset + G_STRUCT_OFFSET (MonoIDT, name_rva));
|
*rva = GUINT32_FROM_LE (assembly->text_rva + assembly->imp_names_offset + 14); /* 14 is hint+strlen+1 of func name */
|
rva = (guint32*)(assembly->code.data + assembly->idt_offset + G_STRUCT_OFFSET (MonoIDT, import_address_table_rva));
|
*rva = GUINT32_FROM_LE (assembly->text_rva + assembly->iat_offset);
|
rva = (guint32*)(assembly->code.data + assembly->idt_offset + G_STRUCT_OFFSET (MonoIDT, import_lookup_table));
|
*rva = GUINT32_FROM_LE (assembly->text_rva + assembly->ilt_offset);
|
|
p = (guchar*)(assembly->code.data + assembly->ilt_offset);
|
value = (assembly->text_rva + assembly->imp_names_offset);
|
*p++ = (value) & 0xff;
|
*p++ = (value >> 8) & (0xff);
|
*p++ = (value >> 16) & (0xff);
|
*p++ = (value >> 24) & (0xff);
|
|
/* the CLI header info */
|
cli_header = (MonoCLIHeader*)(assembly->code.data + assembly->cli_header_offset);
|
cli_header->ch_size = GUINT32_FROM_LE (72);
|
cli_header->ch_runtime_major = GUINT16_FROM_LE (2);
|
cli_header->ch_runtime_minor = GUINT16_FROM_LE (5);
|
cli_header->ch_flags = GUINT32_FROM_LE (assemblyb->pe_kind);
|
if (assemblyb->entry_point) {
|
guint32 table_idx = 0;
|
if (!strcmp (assemblyb->entry_point->object.vtable->klass->name, "MethodBuilder")) {
|
MonoReflectionMethodBuilder *methodb = (MonoReflectionMethodBuilder*)assemblyb->entry_point;
|
table_idx = methodb->table_idx;
|
} else {
|
table_idx = GPOINTER_TO_UINT (g_hash_table_lookup (assembly->method_to_table_idx, assemblyb->entry_point->method));
|
}
|
cli_header->ch_entry_point = GUINT32_FROM_LE (table_idx | MONO_TOKEN_METHOD_DEF);
|
} else {
|
cli_header->ch_entry_point = GUINT32_FROM_LE (0);
|
}
|
/* The embedded managed resources */
|
text_offset = assembly->text_rva + assembly->code.index;
|
cli_header->ch_resources.rva = GUINT32_FROM_LE (text_offset);
|
cli_header->ch_resources.size = GUINT32_FROM_LE (assembly->resources.index);
|
text_offset += assembly->resources.index;
|
cli_header->ch_metadata.rva = GUINT32_FROM_LE (text_offset);
|
cli_header->ch_metadata.size = GUINT32_FROM_LE (assembly->meta_size);
|
text_offset += assembly->meta_size;
|
if (assembly->strong_name_size) {
|
cli_header->ch_strong_name.rva = GUINT32_FROM_LE (text_offset);
|
cli_header->ch_strong_name.size = GUINT32_FROM_LE (assembly->strong_name_size);
|
text_offset += assembly->strong_name_size;
|
}
|
|
/* write the section tables and section content */
|
section = (MonoSectionTable*)(pefile->data + section_start);
|
for (i = 0; i < MONO_SECTION_MAX; ++i) {
|
static const char section_names [][7] = {
|
".text", ".rsrc", ".reloc"
|
};
|
if (!assembly->sections [i].size)
|
continue;
|
strcpy (section->st_name, section_names [i]);
|
/*g_print ("output section %s (%d), size: %d\n", section->st_name, i, assembly->sections [i].size);*/
|
section->st_virtual_address = GUINT32_FROM_LE (assembly->sections [i].rva);
|
section->st_virtual_size = GUINT32_FROM_LE (assembly->sections [i].size);
|
section->st_raw_data_size = GUINT32_FROM_LE (GUINT32_TO_LE (section->st_virtual_size) + (FILE_ALIGN - 1));
|
section->st_raw_data_size &= GUINT32_FROM_LE (~(FILE_ALIGN - 1));
|
section->st_raw_data_ptr = GUINT32_FROM_LE (assembly->sections [i].offset);
|
section->st_flags = GUINT32_FROM_LE (assembly->sections [i].attrs);
|
section ++;
|
}
|
|
checked_write_file (file, pefile->data, pefile->index);
|
|
mono_dynamic_stream_reset (pefile);
|
|
for (i = 0; i < MONO_SECTION_MAX; ++i) {
|
if (!assembly->sections [i].size)
|
continue;
|
|
if (mono_w32file_seek (file, assembly->sections [i].offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
g_error ("mono_w32file_seek returned %d\n", mono_w32error_get_last ());
|
|
switch (i) {
|
case MONO_SECTION_TEXT:
|
/* patch entry point */
|
p = (guchar*)(assembly->code.data + 2);
|
value = (virtual_base + assembly->text_rva + assembly->iat_offset);
|
*p++ = (value) & 0xff;
|
*p++ = (value >> 8) & 0xff;
|
*p++ = (value >> 16) & 0xff;
|
*p++ = (value >> 24) & 0xff;
|
|
checked_write_file (file, assembly->code.data, assembly->code.index);
|
checked_write_file (file, assembly->resources.data, assembly->resources.index);
|
checked_write_file (file, assembly->image.raw_metadata, assembly->meta_size);
|
checked_write_file (file, assembly->strong_name, assembly->strong_name_size);
|
|
|
g_free (assembly->image.raw_metadata);
|
break;
|
case MONO_SECTION_RELOC: {
|
struct {
|
guint32 page_rva;
|
guint32 block_size;
|
guint16 type_and_offset;
|
guint16 term;
|
} reloc;
|
|
g_assert (sizeof (reloc) == 12);
|
|
reloc.page_rva = GUINT32_FROM_LE (assembly->text_rva);
|
reloc.block_size = GUINT32_FROM_LE (12);
|
|
/*
|
* the entrypoint is always at the start of the text section
|
* 3 is IMAGE_REL_BASED_HIGHLOW
|
* 2 is patch_size_rva - text_rva
|
*/
|
reloc.type_and_offset = GUINT16_FROM_LE ((3 << 12) + (2));
|
reloc.term = 0;
|
|
checked_write_file (file, &reloc, sizeof (reloc));
|
|
break;
|
}
|
case MONO_SECTION_RSRC:
|
if (assembly->win32_res) {
|
|
/* Fixup the offsets in the IMAGE_RESOURCE_DATA_ENTRY structures */
|
fixup_resource_directory (assembly->win32_res, assembly->win32_res, assembly->sections [i].rva);
|
checked_write_file (file, assembly->win32_res, assembly->win32_res_size);
|
}
|
break;
|
default:
|
g_assert_not_reached ();
|
}
|
}
|
|
/* check that the file is properly padded */
|
if (mono_w32file_seek (file, file_offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
g_error ("mono_w32file_seek returned %d\n", mono_w32error_get_last ());
|
if (! mono_w32file_truncate (file))
|
g_error ("mono_w32file_truncate returned %d\n", mono_w32error_get_last ());
|
|
mono_dynamic_stream_reset (&assembly->code);
|
mono_dynamic_stream_reset (&assembly->us);
|
mono_dynamic_stream_reset (&assembly->blob);
|
mono_dynamic_stream_reset (&assembly->guid);
|
mono_dynamic_stream_reset (&assembly->sheap);
|
|
g_hash_table_foreach (assembly->blob_cache, (GHFunc)g_free, NULL);
|
g_hash_table_destroy (assembly->blob_cache);
|
assembly->blob_cache = NULL;
|
|
return TRUE;
|
}
|
|
#else /* DISABLE_REFLECTION_EMIT_SAVE */
|
|
gboolean
|
mono_image_create_pefile (MonoReflectionModuleBuilder *mb, HANDLE file, MonoError *error)
|
{
|
g_assert_not_reached ();
|
}
|
|
#endif /* DISABLE_REFLECTION_EMIT_SAVE */
|