Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 9 additions & 4 deletions samples/NativeLibrary/LoadLibrary.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
//On unix make sure to compile using -ldl flag.

//Set this value accordingly to your workspace settings
#define PathToLibrary "./bin/Debug/netstandard2.0/linux-x64/native/NativeLibrary.so"

#if defined(_WIN32)
#define PathToLibrary "bin\\Debug\\net5.0\\win-x64\\native\\NativeLibrary.dll"
#elf defined(__APPLE__)
#define PathToLibrary "./bin/Debug/net5.0/linux-x64/native/NativeLibrary.dylib"
#else
#define PathToLibrary "./bin/Debug/net5.0/linux-x64/native/NativeLibrary.so"
#endif

#ifdef _WIN32
#include "windows.h"
Expand Down Expand Up @@ -57,7 +62,7 @@ int callSumFunc(char *path, char *funcName, int firstInt, int secondInt)
#endif

typedef int(*myFunc)();
myFunc MyImport = symLoad(handle, funcName);
myFunc MyImport = (myFunc)symLoad(handle, funcName);

int result = MyImport(firstInt, secondInt);

Expand All @@ -79,7 +84,7 @@ char *callSumStringFunc(char *path, char *funcName, char *firstString, char *sec
typedef char *(*myFunc)();

// Import Symbol named funcName
myFunc MyImport = symLoad(handle, funcName);
myFunc MyImport = (myFunc)symLoad(handle, funcName);

// The C# function will return a pointer
char *result = MyImport(firstString, secondString);
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/src/nativeaot/Bootstrap/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,11 @@ static struct InitializeRuntimePointerHelper
RhSetRuntimeInitializationCallback(&InitializeRuntime);
}
} initializeRuntimePointerHelper;

extern "C" void* CoreRT_StaticInitialization();

void* CoreRT_StaticInitialization()
{
return &initializeRuntimePointerHelper;
}
#endif // CORERT_DLL
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ The .NET Foundation licenses this file to you under the MIT license.
<LinkerArg Include="-licucore" Condition="'$(TargetOS)' == 'OSX'" />
<LinkerArg Include="-dynamiclib" Condition="'$(TargetOS)' == 'OSX' and '$(NativeLib)' == 'Shared'" />
<LinkerArg Include="-shared" Condition="'$(TargetOS)' != 'OSX' and '$(NativeLib)' == 'Shared'" />
<LinkerArg Include="-Wl,-u,_CoreRT_StaticInitialization" Condition="'$(TargetOS)' == 'OSX' and '$(NativeLib)' == 'Shared'" />
<LinkerArg Include="-Wl,--require-defined,CoreRT_StaticInitialization" Condition="'$(TargetOS)' != 'OSX' and '$(NativeLib)' == 'Shared'" />

<LinkerArg Include="@(NativeFramework->'-framework %(Identity)')" Condition="'$(TargetOS)' == 'OSX'" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ The .NET Foundation licenses this file to you under the MIT license.
<NativeLibrary Include="crypt32.lib" />
</ItemGroup>

<PropertyGroup>
<IlcProcessEntrypoint Condition="$(IlcProcessEntrypoint) == ''">wmainCRTStartup</IlcProcessEntrypoint>
Copy link
Member Author

Choose a reason for hiding this comment

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

I have started by introducing another variable for the SharedLib init entrypoint, but then decided against it as unnecessary abstraction. I think we should wait for a real case why it is useful.

Copy link
Member

Choose a reason for hiding this comment

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

It would be useful for the @kant2002 inspired project file of my Doom fire effect: https://github.com/MichalStrehovsky/sharpfire/blob/fee39991c22b39d263933d0fd1ec25d585d82e6e/nogui.csproj#L4 (that's why I added option to set your own entrypoint when I was messing with it).

I had to avoid specifying WinExe because then the entrypoint gets set up from here and breaks things.

Arguably I never ended up using it because I have better things to do than maintaining my old hacks :).

Two weeks after the hack I learned about /ALTERNATENAME that can be used for this too I guess.

I don't particularly care, just wanted to share the history.

Copy link
Member

Choose a reason for hiding this comment

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

Actually we might be able to use /ALTERNATENAME to remove the need for the /ENTRY line.

Something like /ALTERNATENAME:wWinMainCRTStartup=wmainCRTStartup should also do the trick and we don't even need to condition it on WinExe.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have left it as is. The ALTERNATENAME trick is neat, but i did not look like an improvement to me.

</PropertyGroup>

<ItemGroup>
<LinkerArg Condition="$(NativeLib) == 'Shared'" Include="/DLL" />
<LinkerArg Include="@(NativeLibrary->'&quot;%(Identity)&quot;')" />
Expand All @@ -76,7 +72,8 @@ The .NET Foundation licenses this file to you under the MIT license.
<LinkerArg Include="/INCREMENTAL:NO" />
<LinkerArg Condition="'$(OutputType)' == 'WinExe'" Include="/SUBSYSTEM:WINDOWS" />
<LinkerArg Condition="'$(OutputType)' == 'Exe'" Include="/SUBSYSTEM:CONSOLE" />
<LinkerArg Condition="'$(OutputType)' == 'WinExe' or '$(OutputType)' == 'Exe'" Include="/ENTRY:$(IlcProcessEntrypoint)" />
<LinkerArg Condition="'$(OutputType)' == 'WinExe' or '$(OutputType)' == 'Exe'" Include="/ENTRY:wmainCRTStartup" />
<LinkerArg Condition="$(NativeLib) == 'Shared'" Include="/INCLUDE:CoreRT_StaticInitialization"/>
<LinkerArg Include="/NATVIS:&quot;$(MSBuildThisFileDirectory)CoreRTNatVis.natvis&quot;" />
</ItemGroup>

Expand Down
14 changes: 12 additions & 2 deletions src/coreclr/src/nativeaot/Runtime/startup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,22 @@ void RuntimeThreadShutdown(void* thread)

UNREFERENCED_PARAMETER(thread);

#ifdef TARGET_UNIX
// Some Linux toolset versions call thread-local destructors during shutdown on a wrong thread.
if ((Thread*)thread != ThreadStore::GetCurrentThread())
{
return;
}
#else
ASSERT((Thread*)thread == ThreadStore::GetCurrentThread());
#endif

if (!g_processShutdownHasStarted)
if (g_processShutdownHasStarted)
{
ThreadStore::DetachCurrentThread();
return;
}

ThreadStore::DetachCurrentThread();
}

extern "C" bool RhInitialize()
Expand Down
11 changes: 9 additions & 2 deletions src/tests/Common/CLRTest.NativeAot.targets
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,18 @@ fi
]]>
</NativeAotMultimoduleIncompatibleScript>

<NativeAotBatchScript Condition="'$(CLRTestKind)' == 'BuildAndRun' and '$(NativeAotTest)' != 'false'">
<NativeAotCleanupBatchScript Condition="'$(CLRTestKind)' == 'BuildAndRun' and '$(NativeAotTest)' != 'false'">
<![CDATA[
if defined RunNativeAot (
rem Delete any leftover native compilation bits
IF EXIST native rmdir /s /q native
)
]]>
</NativeAotCleanupBatchScript>

<NativeAotBatchScript Condition="'$(CLRTestKind)' == 'BuildAndRun' and '$(NativeAotTest)' != 'false'">
<![CDATA[
if defined RunNativeAot (

set __Command=!_DebuggerFullPath!
REM Tests run locally need __TestDotNetCmd (set by runtest.py) or a compatible 5.0 dotnet runtime in the path
Expand Down Expand Up @@ -118,7 +125,7 @@ if defined RunNativeAot (
]]>
</NativeAotBatchScript>

<CLRTestBatchPreCommands>$(CLRTestBatchPreCommands);$(NativeAotBatchScript)</CLRTestBatchPreCommands>
<CLRTestBatchPreCommands>$(NativeAotCleanupBatchScript);$(CLRTestBatchPreCommands);$(NativeAotBatchScript)</CLRTestBatchPreCommands>
</PropertyGroup>
</Target>

Expand Down
11 changes: 11 additions & 0 deletions src/tests/nativeaot/SmokeTests/SharedLibrary/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
project (SharedLibrary)
include_directories(${INC_PLATFORM_DIR})

add_executable (SharedLibraryDriver SharedLibrary.cpp)

if (CLR_CMAKE_TARGET_UNIX)
target_link_libraries (SharedLibraryDriver ${CMAKE_DL_LIBS})
endif()

# add the install targets
install (TARGETS PInvokeNative DESTINATION bin)
84 changes: 84 additions & 0 deletions src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#ifdef TARGET_WINDOWS
#include "windows.h"
#else
#include "dlfcn.h"
#endif
#include "stdio.h"
#include "string.h"

#ifndef TARGET_WINDOWS
#define __stdcall
#endif

// typedef for shared lib exported methods
typedef int(__stdcall *f_ReturnsPrimitiveInt)();
typedef bool(__stdcall *f_ReturnsPrimitiveBool)();
typedef char(__stdcall *f_ReturnsPrimitiveChar)();
typedef void(__stdcall *f_EnsureManagedClassLoaders)();

#ifdef TARGET_WINDOWS
int main()
#else
int main(int argc, char* argv[])
#endif
{
#ifdef TARGET_WINDOWS
HINSTANCE handle = LoadLibrary("SharedLibrary.dll");
#elif __APPLE__
void *handle = dlopen(strcat(argv[0], ".dylib"), RTLD_LAZY);
#else
void *handle = dlopen(strcat(argv[0], ".so"), RTLD_LAZY);
#endif

if (!handle)
return 1;

#ifdef TARGET_WINDOWS
f_ReturnsPrimitiveInt returnsPrimitiveInt = (f_ReturnsPrimitiveInt)GetProcAddress(handle, "ReturnsPrimitiveInt");
f_ReturnsPrimitiveBool returnsPrimitiveBool = (f_ReturnsPrimitiveBool)GetProcAddress(handle, "ReturnsPrimitiveBool");
f_ReturnsPrimitiveChar returnsPrimitiveChar = (f_ReturnsPrimitiveChar)GetProcAddress(handle, "ReturnsPrimitiveChar");
f_EnsureManagedClassLoaders ensureManagedClassLoaders = (f_EnsureManagedClassLoaders)GetProcAddress(handle, "EnsureManagedClassLoaders");
f_ReturnsPrimitiveInt checkSimpleGCCollect = (f_ReturnsPrimitiveInt)GetProcAddress(handle, "CheckSimpleGCCollect");
f_ReturnsPrimitiveInt checkSimpleExceptionHandling = (f_ReturnsPrimitiveInt)GetProcAddress(handle, "CheckSimpleExceptionHandling");
#else
f_ReturnsPrimitiveInt returnsPrimitiveInt = (f_ReturnsPrimitiveInt)dlsym(handle, "ReturnsPrimitiveInt");
f_ReturnsPrimitiveBool returnsPrimitiveBool = (f_ReturnsPrimitiveBool)dlsym(handle, "ReturnsPrimitiveBool");
f_ReturnsPrimitiveChar returnsPrimitiveChar = (f_ReturnsPrimitiveChar)dlsym(handle, "ReturnsPrimitiveChar");
f_EnsureManagedClassLoaders ensureManagedClassLoaders = (f_EnsureManagedClassLoaders)dlsym(handle, "EnsureManagedClassLoaders");
f_ReturnsPrimitiveInt checkSimpleGCCollect = (f_ReturnsPrimitiveInt)dlsym(handle, "CheckSimpleGCCollect");
f_ReturnsPrimitiveInt checkSimpleExceptionHandling = (f_ReturnsPrimitiveInt)dlsym(handle, "CheckSimpleExceptionHandling");
#endif

if (returnsPrimitiveInt() != 10)
return 1;

if (!returnsPrimitiveBool())
return 2;

if (returnsPrimitiveChar() != 'a')
return 3;

// As long as no unmanaged exception is thrown
// managed class loaders were initialized successfully
ensureManagedClassLoaders();

if (checkSimpleGCCollect() != 100)
return 4;

if (checkSimpleExceptionHandling() != 100)
return 5;

// CoreRT is not designed to be unloadable, so this won't actually unload the library properly. Verify that attempt
// to unload the library does not to crash at least.
#ifdef TARGET_WINDOWS
FreeLibrary(handle);
#else
// TODO: How to pin the library in memory on Unix?
// dlclose(handle);
#endif

return 100;
}
95 changes: 95 additions & 0 deletions src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace SharedLibrary
{
public class ClassLibrary
{
[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveInt", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static int ReturnsPrimitiveInt()
{
return 10;
}

[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveBool", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static bool ReturnsPrimitiveBool()
{
return true;
}

[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveChar", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static char ReturnsPrimitiveChar()
{
return 'a';
}

[UnmanagedCallersOnly(EntryPoint = "EnsureManagedClassLoaders", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static void EnsureManagedClassLoaders()
{
Random random = new Random();
random.Next();
}

[UnmanagedCallersOnly(EntryPoint = "CheckSimpleExceptionHandling", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static int CheckSimpleExceptionHandling()
{
int result = 10;

try
{
Console.WriteLine("Throwing exception");
throw new Exception();
}
catch when (result == 10)
{
result += 20;
}
finally
{
result += 70;
}

return result;
}

private static bool s_collected;

class ClassWithFinalizer
{
~ClassWithFinalizer() { s_collected = true; }
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void MakeGarbage()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

object[] arr = new object[1024 * 1024];
for (int i = 0; i < arr.Length; i++)
arr[i] = new object();

new ClassWithFinalizer();
}

[UnmanagedCallersOnly(EntryPoint = "CheckSimpleGCCollect", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static int CheckSimpleGCCollect()
{
string myString = string.Format("Hello {0}", "world");

MakeGarbage();

Console.WriteLine("Triggering GC");
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

return s_collected ? (myString == "Hello world" ? 100 : 1) : 2;
}
}
}
38 changes: 38 additions & 0 deletions src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>0</CLRTestPriority>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup>
<NativeAotProjectLines>
<![CDATA[
<PropertyGroup>
<OutputType>Library</OutputType>
<NativeLib>Shared</NativeLib>
</PropertyGroup>
]]>
</NativeAotProjectLines>

<CLRTestBatchPreCommands><![CDATA[
$(CLRTestBatchPreCommands)
mkdir native 2>nul
copy /y SharedLibraryDriver.exe native\SharedLibrary.exe
]]></CLRTestBatchPreCommands>

<BashCLRTestPreCommands><![CDATA[
$(BashCLRTestPreCommands)
mkdir -p native
cp SharedLibraryDriver native/SharedLibrary
]]></BashCLRTestPreCommands>
</PropertyGroup>

<ItemGroup>
<Compile Include="SharedLibrary.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="CMakeLists.txt" />
</ItemGroup>
</Project>