Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
16bfd3a
First version of QCalls implementation, using QCalls on ICU Globaliza…
thaystg Jun 9, 2020
56b4b44
Fixing windows compilation.
thaystg Jun 9, 2020
8455282
Fixing warnings at compilation time.
thaystg Jun 9, 2020
a6b1f48
Fixing iOS compilation.
thaystg Jun 10, 2020
f79c2f1
Fixing iOS compilation.
thaystg Jun 10, 2020
07e609a
Removing implementation of EventPipe using QCalls.
thaystg Jun 12, 2020
26b85db
Fixing usused extern array.
thaystg Jun 12, 2020
d492c57
Adding QCALL to log
thaystg Jun 12, 2020
1b4662b
Fixing getNativeHandle and organising files.
thaystg Jun 15, 2020
b749e18
Fixing compilation error.
thaystg Jun 15, 2020
10101f8
Adding comments, fixing code style.
thaystg Jun 15, 2020
569de74
Fixing Zoltan suggestion.
thaystg Jun 15, 2020
c942e24
Accepting @coffeeflux suggestion.
thaystg Jun 15, 2020
12dca6e
Update src/mono/mono/metadata/native-library.h
thaystg Jun 17, 2020
78e8edb
Update src/mono/mono/metadata/native-library.h
thaystg Jun 17, 2020
e1f488b
Update src/mono/mono/metadata/native-library.h
thaystg Jun 17, 2020
e1e0ed4
Update src/mono/mono/metadata/native-library.h
thaystg Jun 17, 2020
ff6be74
Update src/mono/mono/metadata/native-library.h
thaystg Jun 17, 2020
13dc0d4
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
a203a97
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
9d1992e
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
96cf8b0
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
bdae239
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
5da75d8
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
040048f
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
20c2177
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
8be4f78
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
cc5c49e
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
41d21ab
Update src/mono/mono/metadata/native-library-qcall.c
thaystg Jun 17, 2020
faeb893
Fix mono compilation.
thaystg Jun 18, 2020
70ff810
Fix mono compilation.
thaystg Jun 19, 2020
a50b92c
Fix mono compilation.
thaystg Jun 19, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GCHeapHash.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CastHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\QCallHandles.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\TypeDependencyAttribute.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ namespace System.Runtime.CompilerServices
{
public static partial class RuntimeHelpers
{
// The special dll name to be used for DllImport of QCalls
internal const string QCall = "QCall";

[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,11 @@ private static bool FilterApplyMethodBase(

#region Private\Internal Members

internal IntPtr GetUnderlyingNativeHandle()
{
return m_handle;
}

internal override bool CacheEquals(object? o)
{
return (o is RuntimeType t) && (t.m_handle == m_handle);
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/src/dlls/mscoree/coreclr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ set(CORECLR_LIBRARIES
ildbsymlib
utilcode
v3binder
libraries-native
System.Globalization.Native-Static
interop
)
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/src/libraries-native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,3 @@ include_directories("${CLR_REPO_ROOT_DIR}/src/libraries/Native/Unix/Common")

add_subdirectory(${GLOBALIZATION_NATIVE_DIR} System.Globalization.Native)

add_library(libraries-native
STATIC
entrypoints.c
)
4 changes: 0 additions & 4 deletions src/libraries/Common/src/Interop/Interop.Libraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ internal static partial class Interop
{
internal static partial class Libraries
{
#if MONO
internal const string GlobalizationNative = "__Internal";
#else
internal const string GlobalizationNative = "QCall";
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ set(NATIVEGLOBALIZATION_SOURCES
pal_normalization.c
pal_timeZoneInfo.c
pal_icushim.c
entrypoints.c
)

include_directories("../Common")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\TraceLogging\TraceLoggingTypeInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\TraceLogging\TypeAnalysis.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\TraceLogging\XplatEventLogger.cs" Condition="'$(FeatureXplatEventSource)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\QCallHandles.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)\..\..\Common\src\System\HexConverter.cs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ internal QCallTypeHandle(ref System.RuntimeType type)
{
_ptr = Unsafe.AsPointer(ref type);
if (type != null)
_handle = type.m_handle;
_handle = type.GetUnderlyingNativeHandle();
else
_handle = IntPtr.Zero;
}

internal QCallTypeHandle(ref System.RuntimeTypeHandle rth)
: this(ref rth.m_type)
{
_ptr = Unsafe.AsPointer(ref rth);
_handle = rth.Value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace System.Runtime.CompilerServices
{
public static partial class RuntimeHelpers
{
// The special dll name to be used for DllImport of QCalls
internal const string QCall = "QCall";

public delegate void TryCode(object? userData);

public delegate void CleanupCode(object? userData, bool exceptionThrown);
Expand Down
14 changes: 11 additions & 3 deletions src/mono/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -6825,11 +6825,11 @@ if test x$with_core = xonly; then
[AC_MSG_ERROR([Cannot find libicucore, skipping build for System.Globalization.Native. .NET globalization is not expected to function.])])
AC_CHECK_HEADER(unicode/utypes.h, [have_sys_icu=yes], [have_sys_icu=no])
if test x$have_sys_icu = xyes; then
ICU_CFLAGS="$CPPFLAGS -DOSX_ICU_LIBRARY_PATH=AS_ESCAPE(\"/usr/lib/libicucore.dylib\", '\"') -DTARGET_UNIX -DU_DISABLE_RENAMING -Wno-reserved-id-macro -Wno-documentation -Wno-documentation-unknown-command -Wno-switch-enum -Wno-covered-switch-default -Wno-covered-switch-default -Wno-extra-semi-stmt -Wno-unknown-warning-option -Wno-deprecated-declarations"
ICU_CFLAGS="$CPPFLAGS -DPALEXPORT="" -DOSX_ICU_LIBRARY_PATH=AS_ESCAPE(\"/usr/lib/libicucore.dylib\", '\"') -DTARGET_UNIX -DU_DISABLE_RENAMING -Wno-reserved-id-macro -Wno-documentation -Wno-documentation-unknown-command -Wno-switch-enum -Wno-covered-switch-default -Wno-covered-switch-default -Wno-extra-semi-stmt -Wno-unknown-warning-option -Wno-deprecated-declarations"
fi
CPPFLAGS=$ORIG_CPPFLAGS
elif test x$platform_android = xyes; then
ICU_CFLAGS="-DHAVE_UDAT_STANDALONE_SHORTER_WEEKDAYS -DHAVE_SET_MAX_VARIABLE -DTARGET_UNIX -DTARGET_ANDROID -Wno-reserved-id-macro -Wno-documentation -Wno-documentation-unknown-command -Wno-switch-enum -Wno-covered-switch-default -Wno-covered-switch-default -Wno-extra-semi-stmt -Wno-unknown-warning-option"
ICU_CFLAGS="-DPALEXPORT="" -DHAVE_UDAT_STANDALONE_SHORTER_WEEKDAYS -DHAVE_SET_MAX_VARIABLE -DTARGET_UNIX -DTARGET_ANDROID -Wno-reserved-id-macro -Wno-documentation -Wno-documentation-unknown-command -Wno-switch-enum -Wno-covered-switch-default -Wno-covered-switch-default -Wno-extra-semi-stmt -Wno-unknown-warning-option"
have_sys_icu=yes
elif test x$host_linux = xyes; then
AC_CHECK_LIB(icuuc, main, LIBS=$LIBS,
Expand All @@ -6838,12 +6838,20 @@ if test x$with_core = xonly; then
[AC_MSG_ERROR([Cannot find libicui18n, try installing libicu-dev (or the appropriate package for your platform).])])
AC_CHECK_HEADER(unicode/utypes.h, [have_sys_icu=yes], [have_sys_icu=no])
if test x$have_sys_icu = xyes; then
ICU_CFLAGS="-DTARGET_UNIX -Wno-reserved-id-macro -Wno-documentation -Wno-documentation-unknown-command -Wno-switch-enum -Wno-covered-switch-default -Wno-covered-switch-default -Wno-extra-semi-stmt -Wno-unknown-warning-option"
ICU_CFLAGS="-DPALEXPORT="" -DTARGET_UNIX -Wno-reserved-id-macro -Wno-documentation -Wno-documentation-unknown-command -Wno-switch-enum -Wno-covered-switch-default -Wno-covered-switch-default -Wno-extra-semi-stmt -Wno-unknown-warning-option"
fi
else
GLOBALIZATION_SHIM_DEFINES="-DNO_GLOBALIZATION_SHIM"
fi
AC_SUBST(ICU_CFLAGS)
else
GLOBALIZATION_SHIM_DEFINES="-DNO_GLOBALIZATION_SHIM"
fi
else
GLOBALIZATION_SHIM_DEFINES="-DNO_GLOBALIZATION_SHIM"
fi

AC_SUBST(GLOBALIZATION_SHIM_DEFINES)
AC_SUBST(ICU_SHIM_PATH)
AM_CONDITIONAL(HAVE_SYS_ICU, test x$have_sys_icu = xyes)

Expand Down
13 changes: 8 additions & 5 deletions src/mono/mono/metadata/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,10 @@ nodist_libmonoruntime_shimglobalization_la_SOURCES = \
@ICU_SHIM_PATH@/pal_localeStringData.c \
@ICU_SHIM_PATH@/pal_normalization.c \
@ICU_SHIM_PATH@/pal_timeZoneInfo.c \
@ICU_SHIM_PATH@/pal_icushim.c
@ICU_SHIM_PATH@/pal_icushim.c \
@ICU_SHIM_PATH@/entrypoints.c

libmonoruntime_shimglobalization_la_CFLAGS = @ICU_CFLAGS@ -I$(top_srcdir)/../libraries/Native/Unix/Common/
libmonoruntime_shimglobalization_la_CFLAGS = @ICU_CFLAGS@ -I$(top_srcdir)/../libraries/Native/Unix/System.Globalization.Native/ -I$(top_srcdir)/../libraries/Native/Unix/Common/
endif
endif

Expand Down Expand Up @@ -413,7 +414,9 @@ common_sources = \
callspec.h \
callspec.c \
abi.c \
native-library.c
native-library.c \
native-library.h \
native-library-qcall.c

# These source files have compile time dependencies on GC code
gc_dependent_sources = \
Expand Down Expand Up @@ -449,12 +452,12 @@ if !ENABLE_MSVC_ONLY

libmonoruntime_la_SOURCES = $(common_sources) $(icall_tables_sources) $(ilgen_sources) $(gc_dependent_sources) $(null_gc_sources) $(boehm_sources)
# Add CXX_ADD_CFLAGS per-library until/unless https://github.com/dotnet/corefx/pull/31342.
libmonoruntime_la_CFLAGS = $(BOEHM_DEFINES) @CXX_ADD_CFLAGS@
libmonoruntime_la_CFLAGS = $(BOEHM_DEFINES) $(GLOBALIZATION_SHIM_DEFINES) @CXX_ADD_CFLAGS@
libmonoruntime_la_LIBADD = libmonoruntime-config.la $(culture_libraries) $(support_libraries) $(shim_libraries)

libmonoruntimesgen_la_SOURCES = $(common_sources) $(icall_tables_sources) $(ilgen_sources) $(gc_dependent_sources) $(sgen_sources)
# Add CXX_ADD_CFLAGS per-library until/unless https://github.com/dotnet/corefx/pull/31342.
libmonoruntimesgen_la_CFLAGS = $(SGEN_DEFINES) @CXX_ADD_CFLAGS@
libmonoruntimesgen_la_CFLAGS = $(SGEN_DEFINES) $(GLOBALIZATION_SHIM_DEFINES) @CXX_ADD_CFLAGS@
libmonoruntimesgen_la_LIBADD = libmonoruntime-config.la $(culture_libraries) $(support_libraries) $(shim_libraries)

endif # !ENABLE_MSVC_ONLY
Expand Down
122 changes: 122 additions & 0 deletions src/mono/mono/metadata/native-library-qcall.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "config.h"
#include "mono/metadata/assembly-internals.h"
#include "mono/metadata/class-internals.h"
#include "mono/metadata/icall-decl.h"
#include "mono/metadata/loader-internals.h"
#include "mono/metadata/loader.h"
#include "mono/metadata/object-internals.h"
#include "mono/metadata/reflection-internals.h"
#include "mono/utils/checked-build.h"
#include "mono/utils/mono-compiler.h"
#include "mono/utils/mono-logger-internals.h"
#include "mono/utils/mono-path.h"
#include "mono/metadata/native-library.h"

extern const void* gPalGlobalizationNative[];

enum {
func_flag_end_of_array = 0x01,
func_flag_has_signature = 0x02,
func_flag_unreferenced = 0x04, // Suppress unused fcall check
func_flag_qcall = 0x08, // QCall - mscorlib.dll to mscorwks.dll transition implemented as PInvoke
};

#if defined(NO_GLOBALIZATION_SHIM) || !defined(ENABLE_NETCORE)
const void* gPalGlobalizationNative[] = { (void*)func_flag_end_of_array };
#endif

static const MonoQCallDef c_qcalls[] =
{
#define FCClassElement(name,namespace,funcs) {name, namespace, funcs},
#include "mono/metadata/qcall-def.h"
#undef FCClassElement
};

const int c_nECClasses = sizeof (c_qcalls) / sizeof (c_qcalls[0]);

static gboolean is_end_of_array (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_end_of_array); }
static gboolean has_signature (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_has_signature); }
static gboolean is_unreferenced (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_unreferenced); }
static gboolean is_qcall (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_qcall); }
//CorInfoIntrinsics IntrinsicID(ECFunc *func) { return (CorInfoIntrinsics)((INT8)(func->m_dwFlags >> 16)); }
//int DynamicID(ECFunc *func) { return (int) ((int8)(func->m_dwFlags >> 24)); }

static MonoQCallFunc *
next_in_array (MonoQCallFunc *func)
{
return (MonoQCallFunc *)((char *)func +sizeof (MonoQCallFunc));
//(HasSignature(func) ? sizeof(ECFunc) : offsetof(ECFunc, func->m_pMethodSig)));
}

static int
find_impls_index_for_class (MonoMethod *method)
{
const char *namespace_name = m_class_get_name_space (method->klass);
const char *name = m_class_get_name (method->klass);

if (name == NULL)
return -1;

unsigned low = 0;
unsigned high = c_nECClasses;

#ifdef DEBUG
static bool checkedSort = FALSE;
if (!checkedSort) {
checkedSort = TRUE;
for (unsigned i = 1; i < high; i++) {
int cmp = strcmp (c_qcalls[i].class_name, c_qcalls[i-1].class_name);
if (cmp == 0)
cmp = strcmp (c_qcalls[i].namespace_name, c_qcalls[i-1].namespace_name);
g_assert (cmp > 0);
}
}
#endif // DEBUG
while (high > low) {
unsigned mid = (high + low) / 2;
int cmp = strcmp (name, c_qcalls[mid].class_name);
if (cmp == 0)
cmp = strcmp (namespace_name, c_qcalls[mid].namespace_name);

if (cmp == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: don't need the brackets here.

return mid;
}
if (cmp > 0)
low = mid + 1;
else
high = mid;
}
return -1;
}

static int
find_index_for_method (MonoMethod *method, const void **impls)
{
const char *method_name = method->name;
for (MonoQCallFunc *cur = (MonoQCallFunc *)impls; !is_end_of_array (cur); cur = next_in_array (cur))
{
if (strcmp (cur->method_name, method_name) != 0)
continue;
return (int)((const void**)cur - impls);
}

return -1;
}

gpointer
mono_lookup_pinvoke_qcall_internal (MonoMethod *method, MonoLookupPInvokeStatus *status_out)
{
int pos_class = find_impls_index_for_class (method);
if (pos_class < 0) {
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_QCALL,
"Couldn't find class: '%s' in namespace '%s'.", m_class_get_name (method->klass), m_class_get_name_space (method->klass));
return NULL;
}
int pos_method = find_index_for_method (method, c_qcalls[pos_class].functions);
if (pos_method < 0) {
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_QCALL,
"Couldn't find method: '%s' in class '%s' in namespace '%s'.", method->name, m_class_get_name (method->klass), m_class_get_name_space (method->klass));
return NULL;
}
return (gpointer)c_qcalls[pos_class].functions[pos_method+1];
}
26 changes: 7 additions & 19 deletions src/mono/mono/metadata/native-library.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "mono/utils/mono-compiler.h"
#include "mono/utils/mono-logger-internals.h"
#include "mono/utils/mono-path.h"
#include "mono/metadata/native-library.h"

#ifdef ENABLE_NETCORE
static int pinvoke_search_directories_count;
Expand Down Expand Up @@ -52,25 +53,6 @@ static GSList *bundle_library_paths;
// Directory where we unpacked dynamic libraries
static char *bundled_dylibrary_directory;

typedef enum {
LOOKUP_PINVOKE_ERR_OK = 0, /* No error */
LOOKUP_PINVOKE_ERR_NO_LIB, /* DllNotFoundException */
LOOKUP_PINVOKE_ERR_NO_SYM, /* EntryPointNotFoundException */
} MonoLookupPInvokeErr;

/* We should just use a MonoError, but mono_lookup_pinvoke_call has this legacy
* error reporting mechanism where it returns an exception class and a string
* message. So instead we return an error code and message, and for internal
* callers convert it to a MonoError.
*
* Don't expose this type to the runtime. It's just an implementation
* detail for backward compatability.
*/
typedef struct MonoLookupPInvokeStatus {
MonoLookupPInvokeErr err_code;
char *err_arg;
} MonoLookupPInvokeStatus;

/* Class lazy loading functions */
GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
GENERATE_TRY_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
Expand Down Expand Up @@ -1249,6 +1231,11 @@ lookup_pinvoke_call_impl (MonoMethod *method, MonoLookupPInvokeStatus *status_ou
new_import = g_strdup (orig_import);
#endif

if (strcmp (new_scope, "QCall") == 0) {
piinfo->addr = mono_lookup_pinvoke_qcall_internal (method, status_out);
return piinfo->addr;
}

#ifdef ENABLE_NETCORE
#ifndef HOST_WIN32
retry_with_libcoreclr:
Expand Down Expand Up @@ -1614,3 +1601,4 @@ mono_loader_save_bundled_library (int fd, uint64_t offset, uint64_t size, const

g_free (buffer);
}

47 changes: 47 additions & 0 deletions src/mono/mono/metadata/native-library.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifndef _MONO_METADATA_NATIVE_LIBRARY_H_
#define _MONO_METADATA_NATIVE_LIBRARY_H_

#include <glib.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/image.h>
#include <mono/metadata/object-forward.h>
#include <mono/utils/mono-forward.h>
#include <mono/utils/mono-error.h>
#include <mono/utils/mono-coop-mutex.h>

typedef enum {
LOOKUP_PINVOKE_ERR_OK = 0, /* No error */
LOOKUP_PINVOKE_ERR_NO_LIB, /* DllNotFoundException */
LOOKUP_PINVOKE_ERR_NO_SYM, /* EntryPointNotFoundException */
} MonoLookupPInvokeErr;

/* We should just use a MonoError, but mono_lookup_pinvoke_call has this legacy
* error reporting mechanism where it returns an exception class and a string
* message. So instead we return an error code and message, and for internal
* callers convert it to a MonoError.
*
* Don't expose this type to the runtime. It's just an implementation
* detail for backward compatability.
*/
typedef struct MonoLookupPInvokeStatus {
MonoLookupPInvokeErr err_code;
char *err_arg;
} MonoLookupPInvokeStatus;

gpointer
mono_lookup_pinvoke_qcall_internal (MonoMethod *method, MonoLookupPInvokeStatus *error);

typedef struct MonoQCallDef
{
const char *class_name;
const char *namespace_name;
const void **functions;
} MonoQCallDef;

typedef struct MonoQCallFunc {
intptr_t flags; //legal values (0x01 - end of array mareker, 0x08 - qcall)
void *implementation;
const char *method_name;
} MonoQCallFunc;

#endif
Loading