Skip to content

Commit cb591ee

Browse files
authored
Fix native library sample (#270)
* Fix native library sample * Add smoke test from CoreRT * Workaround for Linux bug * Workaround test harness quirks
1 parent 1187979 commit cb591ee

File tree

10 files changed

+270
-13
lines changed

10 files changed

+270
-13
lines changed

samples/NativeLibrary/LoadLibrary.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44
//On unix make sure to compile using -ldl flag.
55

66
//Set this value accordingly to your workspace settings
7-
#define PathToLibrary "./bin/Debug/netstandard2.0/linux-x64/native/NativeLibrary.so"
8-
7+
#if defined(_WIN32)
8+
#define PathToLibrary "bin\\Debug\\net5.0\\win-x64\\native\\NativeLibrary.dll"
9+
#elf defined(__APPLE__)
10+
#define PathToLibrary "./bin/Debug/net5.0/linux-x64/native/NativeLibrary.dylib"
11+
#else
12+
#define PathToLibrary "./bin/Debug/net5.0/linux-x64/native/NativeLibrary.so"
13+
#endif
914

1015
#ifdef _WIN32
1116
#include "windows.h"
@@ -57,7 +62,7 @@ int callSumFunc(char *path, char *funcName, int firstInt, int secondInt)
5762
#endif
5863

5964
typedef int(*myFunc)();
60-
myFunc MyImport = symLoad(handle, funcName);
65+
myFunc MyImport = (myFunc)symLoad(handle, funcName);
6166

6267
int result = MyImport(firstInt, secondInt);
6368

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

8186
// Import Symbol named funcName
82-
myFunc MyImport = symLoad(handle, funcName);
87+
myFunc MyImport = (myFunc)symLoad(handle, funcName);
8388

8489
// The C# function will return a pointer
8590
char *result = MyImport(firstString, secondString);

src/coreclr/src/nativeaot/Bootstrap/main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,11 @@ static struct InitializeRuntimePointerHelper
199199
RhSetRuntimeInitializationCallback(&InitializeRuntime);
200200
}
201201
} initializeRuntimePointerHelper;
202+
203+
extern "C" void* CoreRT_StaticInitialization();
204+
205+
void* CoreRT_StaticInitialization()
206+
{
207+
return &initializeRuntimePointerHelper;
208+
}
202209
#endif // CORERT_DLL

src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ The .NET Foundation licenses this file to you under the MIT license.
8585
<LinkerArg Include="-licucore" Condition="'$(TargetOS)' == 'OSX'" />
8686
<LinkerArg Include="-dynamiclib" Condition="'$(TargetOS)' == 'OSX' and '$(NativeLib)' == 'Shared'" />
8787
<LinkerArg Include="-shared" Condition="'$(TargetOS)' != 'OSX' and '$(NativeLib)' == 'Shared'" />
88+
<LinkerArg Include="-Wl,-u,_CoreRT_StaticInitialization" Condition="'$(TargetOS)' == 'OSX' and '$(NativeLib)' == 'Shared'" />
89+
<LinkerArg Include="-Wl,--require-defined,CoreRT_StaticInitialization" Condition="'$(TargetOS)' != 'OSX' and '$(NativeLib)' == 'Shared'" />
90+
8891
<LinkerArg Include="@(NativeFramework->'-framework %(Identity)')" Condition="'$(TargetOS)' == 'OSX'" />
8992
</ItemGroup>
9093

src/coreclr/src/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.props

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ The .NET Foundation licenses this file to you under the MIT license.
6363
<NativeLibrary Include="crypt32.lib" />
6464
</ItemGroup>
6565

66-
<PropertyGroup>
67-
<IlcProcessEntrypoint Condition="$(IlcProcessEntrypoint) == ''">wmainCRTStartup</IlcProcessEntrypoint>
68-
</PropertyGroup>
69-
7066
<ItemGroup>
7167
<LinkerArg Condition="$(NativeLib) == 'Shared'" Include="/DLL" />
7268
<LinkerArg Include="@(NativeLibrary->'&quot;%(Identity)&quot;')" />
@@ -76,7 +72,8 @@ The .NET Foundation licenses this file to you under the MIT license.
7672
<LinkerArg Include="/INCREMENTAL:NO" />
7773
<LinkerArg Condition="'$(OutputType)' == 'WinExe'" Include="/SUBSYSTEM:WINDOWS" />
7874
<LinkerArg Condition="'$(OutputType)' == 'Exe'" Include="/SUBSYSTEM:CONSOLE" />
79-
<LinkerArg Condition="'$(OutputType)' == 'WinExe' or '$(OutputType)' == 'Exe'" Include="/ENTRY:$(IlcProcessEntrypoint)" />
75+
<LinkerArg Condition="'$(OutputType)' == 'WinExe' or '$(OutputType)' == 'Exe'" Include="/ENTRY:wmainCRTStartup" />
76+
<LinkerArg Condition="$(NativeLib) == 'Shared'" Include="/INCLUDE:CoreRT_StaticInitialization"/>
8077
<LinkerArg Include="/NATVIS:&quot;$(MSBuildThisFileDirectory)CoreRTNatVis.natvis&quot;" />
8178
</ItemGroup>
8279

src/coreclr/src/nativeaot/Runtime/startup.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,22 @@ void RuntimeThreadShutdown(void* thread)
378378

379379
UNREFERENCED_PARAMETER(thread);
380380

381+
#ifdef TARGET_UNIX
382+
// Some Linux toolset versions call thread-local destructors during shutdown on a wrong thread.
383+
if ((Thread*)thread != ThreadStore::GetCurrentThread())
384+
{
385+
return;
386+
}
387+
#else
381388
ASSERT((Thread*)thread == ThreadStore::GetCurrentThread());
389+
#endif
382390

383-
if (!g_processShutdownHasStarted)
391+
if (g_processShutdownHasStarted)
384392
{
385-
ThreadStore::DetachCurrentThread();
393+
return;
386394
}
395+
396+
ThreadStore::DetachCurrentThread();
387397
}
388398

389399
extern "C" bool RhInitialize()

src/tests/Common/CLRTest.NativeAot.targets

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,18 @@ fi
8282
]]>
8383
</NativeAotMultimoduleIncompatibleScript>
8484

85-
<NativeAotBatchScript Condition="'$(CLRTestKind)' == 'BuildAndRun' and '$(NativeAotTest)' != 'false'">
85+
<NativeAotCleanupBatchScript Condition="'$(CLRTestKind)' == 'BuildAndRun' and '$(NativeAotTest)' != 'false'">
8686
<![CDATA[
8787
if defined RunNativeAot (
8888
rem Delete any leftover native compilation bits
8989
IF EXIST native rmdir /s /q native
90+
)
91+
]]>
92+
</NativeAotCleanupBatchScript>
93+
94+
<NativeAotBatchScript Condition="'$(CLRTestKind)' == 'BuildAndRun' and '$(NativeAotTest)' != 'false'">
95+
<![CDATA[
96+
if defined RunNativeAot (
9097
9198
set __Command=!_DebuggerFullPath!
9299
REM Tests run locally need __TestDotNetCmd (set by runtest.py) or a compatible 5.0 dotnet runtime in the path
@@ -118,7 +125,7 @@ if defined RunNativeAot (
118125
]]>
119126
</NativeAotBatchScript>
120127

121-
<CLRTestBatchPreCommands>$(CLRTestBatchPreCommands);$(NativeAotBatchScript)</CLRTestBatchPreCommands>
128+
<CLRTestBatchPreCommands>$(NativeAotCleanupBatchScript);$(CLRTestBatchPreCommands);$(NativeAotBatchScript)</CLRTestBatchPreCommands>
122129
</PropertyGroup>
123130
</Target>
124131

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
project (SharedLibrary)
2+
include_directories(${INC_PLATFORM_DIR})
3+
4+
add_executable (SharedLibraryDriver SharedLibrary.cpp)
5+
6+
if (CLR_CMAKE_TARGET_UNIX)
7+
target_link_libraries (SharedLibraryDriver ${CMAKE_DL_LIBS})
8+
endif()
9+
10+
# add the install targets
11+
install (TARGETS PInvokeNative DESTINATION bin)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#ifdef TARGET_WINDOWS
5+
#include "windows.h"
6+
#else
7+
#include "dlfcn.h"
8+
#endif
9+
#include "stdio.h"
10+
#include "string.h"
11+
12+
#ifndef TARGET_WINDOWS
13+
#define __stdcall
14+
#endif
15+
16+
// typedef for shared lib exported methods
17+
typedef int(__stdcall *f_ReturnsPrimitiveInt)();
18+
typedef bool(__stdcall *f_ReturnsPrimitiveBool)();
19+
typedef char(__stdcall *f_ReturnsPrimitiveChar)();
20+
typedef void(__stdcall *f_EnsureManagedClassLoaders)();
21+
22+
#ifdef TARGET_WINDOWS
23+
int main()
24+
#else
25+
int main(int argc, char* argv[])
26+
#endif
27+
{
28+
#ifdef TARGET_WINDOWS
29+
HINSTANCE handle = LoadLibrary("SharedLibrary.dll");
30+
#elif __APPLE__
31+
void *handle = dlopen(strcat(argv[0], ".dylib"), RTLD_LAZY);
32+
#else
33+
void *handle = dlopen(strcat(argv[0], ".so"), RTLD_LAZY);
34+
#endif
35+
36+
if (!handle)
37+
return 1;
38+
39+
#ifdef TARGET_WINDOWS
40+
f_ReturnsPrimitiveInt returnsPrimitiveInt = (f_ReturnsPrimitiveInt)GetProcAddress(handle, "ReturnsPrimitiveInt");
41+
f_ReturnsPrimitiveBool returnsPrimitiveBool = (f_ReturnsPrimitiveBool)GetProcAddress(handle, "ReturnsPrimitiveBool");
42+
f_ReturnsPrimitiveChar returnsPrimitiveChar = (f_ReturnsPrimitiveChar)GetProcAddress(handle, "ReturnsPrimitiveChar");
43+
f_EnsureManagedClassLoaders ensureManagedClassLoaders = (f_EnsureManagedClassLoaders)GetProcAddress(handle, "EnsureManagedClassLoaders");
44+
f_ReturnsPrimitiveInt checkSimpleGCCollect = (f_ReturnsPrimitiveInt)GetProcAddress(handle, "CheckSimpleGCCollect");
45+
f_ReturnsPrimitiveInt checkSimpleExceptionHandling = (f_ReturnsPrimitiveInt)GetProcAddress(handle, "CheckSimpleExceptionHandling");
46+
#else
47+
f_ReturnsPrimitiveInt returnsPrimitiveInt = (f_ReturnsPrimitiveInt)dlsym(handle, "ReturnsPrimitiveInt");
48+
f_ReturnsPrimitiveBool returnsPrimitiveBool = (f_ReturnsPrimitiveBool)dlsym(handle, "ReturnsPrimitiveBool");
49+
f_ReturnsPrimitiveChar returnsPrimitiveChar = (f_ReturnsPrimitiveChar)dlsym(handle, "ReturnsPrimitiveChar");
50+
f_EnsureManagedClassLoaders ensureManagedClassLoaders = (f_EnsureManagedClassLoaders)dlsym(handle, "EnsureManagedClassLoaders");
51+
f_ReturnsPrimitiveInt checkSimpleGCCollect = (f_ReturnsPrimitiveInt)dlsym(handle, "CheckSimpleGCCollect");
52+
f_ReturnsPrimitiveInt checkSimpleExceptionHandling = (f_ReturnsPrimitiveInt)dlsym(handle, "CheckSimpleExceptionHandling");
53+
#endif
54+
55+
if (returnsPrimitiveInt() != 10)
56+
return 1;
57+
58+
if (!returnsPrimitiveBool())
59+
return 2;
60+
61+
if (returnsPrimitiveChar() != 'a')
62+
return 3;
63+
64+
// As long as no unmanaged exception is thrown
65+
// managed class loaders were initialized successfully
66+
ensureManagedClassLoaders();
67+
68+
if (checkSimpleGCCollect() != 100)
69+
return 4;
70+
71+
if (checkSimpleExceptionHandling() != 100)
72+
return 5;
73+
74+
// CoreRT is not designed to be unloadable, so this won't actually unload the library properly. Verify that attempt
75+
// to unload the library does not to crash at least.
76+
#ifdef TARGET_WINDOWS
77+
FreeLibrary(handle);
78+
#else
79+
// TODO: How to pin the library in memory on Unix?
80+
// dlclose(handle);
81+
#endif
82+
83+
return 100;
84+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
8+
namespace SharedLibrary
9+
{
10+
public class ClassLibrary
11+
{
12+
[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveInt", CallConvs = new Type[] { typeof(CallConvStdcall) })]
13+
public static int ReturnsPrimitiveInt()
14+
{
15+
return 10;
16+
}
17+
18+
[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveBool", CallConvs = new Type[] { typeof(CallConvStdcall) })]
19+
public static bool ReturnsPrimitiveBool()
20+
{
21+
return true;
22+
}
23+
24+
[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveChar", CallConvs = new Type[] { typeof(CallConvStdcall) })]
25+
public static char ReturnsPrimitiveChar()
26+
{
27+
return 'a';
28+
}
29+
30+
[UnmanagedCallersOnly(EntryPoint = "EnsureManagedClassLoaders", CallConvs = new Type[] { typeof(CallConvStdcall) })]
31+
public static void EnsureManagedClassLoaders()
32+
{
33+
Random random = new Random();
34+
random.Next();
35+
}
36+
37+
[UnmanagedCallersOnly(EntryPoint = "CheckSimpleExceptionHandling", CallConvs = new Type[] { typeof(CallConvStdcall) })]
38+
public static int CheckSimpleExceptionHandling()
39+
{
40+
int result = 10;
41+
42+
try
43+
{
44+
Console.WriteLine("Throwing exception");
45+
throw new Exception();
46+
}
47+
catch when (result == 10)
48+
{
49+
result += 20;
50+
}
51+
finally
52+
{
53+
result += 70;
54+
}
55+
56+
return result;
57+
}
58+
59+
private static bool s_collected;
60+
61+
class ClassWithFinalizer
62+
{
63+
~ClassWithFinalizer() { s_collected = true; }
64+
}
65+
66+
[MethodImpl(MethodImplOptions.NoInlining)]
67+
private static void MakeGarbage()
68+
{
69+
GC.Collect();
70+
GC.WaitForPendingFinalizers();
71+
GC.Collect();
72+
73+
object[] arr = new object[1024 * 1024];
74+
for (int i = 0; i < arr.Length; i++)
75+
arr[i] = new object();
76+
77+
new ClassWithFinalizer();
78+
}
79+
80+
[UnmanagedCallersOnly(EntryPoint = "CheckSimpleGCCollect", CallConvs = new Type[] { typeof(CallConvStdcall) })]
81+
public static int CheckSimpleGCCollect()
82+
{
83+
string myString = string.Format("Hello {0}", "world");
84+
85+
MakeGarbage();
86+
87+
Console.WriteLine("Triggering GC");
88+
GC.Collect();
89+
GC.WaitForPendingFinalizers();
90+
GC.Collect();
91+
92+
return s_collected ? (myString == "Hello world" ? 100 : 1) : 2;
93+
}
94+
}
95+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Library</OutputType>
4+
<CLRTestKind>BuildAndRun</CLRTestKind>
5+
<CLRTestPriority>0</CLRTestPriority>
6+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7+
</PropertyGroup>
8+
9+
<PropertyGroup>
10+
<NativeAotProjectLines>
11+
<![CDATA[
12+
<PropertyGroup>
13+
<OutputType>Library</OutputType>
14+
<NativeLib>Shared</NativeLib>
15+
</PropertyGroup>
16+
]]>
17+
</NativeAotProjectLines>
18+
19+
<CLRTestBatchPreCommands><![CDATA[
20+
$(CLRTestBatchPreCommands)
21+
mkdir native 2>nul
22+
copy /y SharedLibraryDriver.exe native\SharedLibrary.exe
23+
]]></CLRTestBatchPreCommands>
24+
25+
<BashCLRTestPreCommands><![CDATA[
26+
$(BashCLRTestPreCommands)
27+
mkdir -p native
28+
cp SharedLibraryDriver native/SharedLibrary
29+
]]></BashCLRTestPreCommands>
30+
</PropertyGroup>
31+
32+
<ItemGroup>
33+
<Compile Include="SharedLibrary.cs" />
34+
</ItemGroup>
35+
<ItemGroup>
36+
<ProjectReference Include="CMakeLists.txt" />
37+
</ItemGroup>
38+
</Project>

0 commit comments

Comments
 (0)