Skip to content

Commit

Permalink
[runtime/tools] Implement finding native support libraries when linki…
Browse files Browse the repository at this point in the history
…ng statically. Fixes xamarin#10950, xamarin#11145 and xamarin#12100.

* Add support for Mono Components.

* Modifies how we look up symbols from native libraries shipped with Mono:

This also meant propagating how libmono is linked from the MSBuild code to the Application
class so that our existing logic is able to correctly determine which native mono
lib to use.

Fixes xamarin#10950.
Fixes xamarin#11145.
Fixes xamarin#12100.
  • Loading branch information
rolfbjarne committed Jul 26, 2021
1 parent 478b588 commit 5975c8b
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 38 deletions.
37 changes: 32 additions & 5 deletions dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,22 @@
</Touch>
</Target>

<Target Name="_ComputeLinkerArguments" DependsOnTargets="_ComputeLinkMode;_ComputeFrameworkVariables;_ComputeFrameworkAssemblies;ComputeResolvedFilesToPublishList;_ParseBundlerArguments;_ComputeVariables;_CreateRuntimeConfiguration">
<Target Name="_ComputeMonoComponents" BeforeTargets="_MonoSelectRuntimeComponents">
<ItemGroup>
<_MonoComponent Include="hot_reload" Condition="'$(MtouchInterpreter)' == 'true'" />
<_MonoComponent Include="debugger" Condition="'$(_BundlerDebug)' == 'true'" />
<_MonoComponent Include="diagnostics_tracing" Condition="'$(_BundlerDebug)' == 'true'" />
</ItemGroup>
</Target>

<Target Name="_ComputeMonoLibraries" DependsOnTargets="_ComputeMonoComponents">
<ItemGroup>
<!-- Remove files mono told us not to link with -->
<_MonoLibrary Remove="@(_MonoRuntimeComponentDontLink -> '$(_MonoRuntimePackPath)/native/%(Identity)')" />
</ItemGroup>
</Target>

<Target Name="_ComputeLinkerArguments" DependsOnTargets="_ComputeLinkMode;_ComputeFrameworkVariables;_ComputeFrameworkAssemblies;ComputeResolvedFilesToPublishList;_ParseBundlerArguments;_ComputeVariables;_CreateRuntimeConfiguration;_MonoSelectRuntimeComponents">
<!-- Validate the linker mode -->
<Error Text="Invalid link mode: '$(_LinkMode)'. Valid link modes are: 'None', 'SdkOnly' and 'Full'" Condition="'$(_LinkMode)' != 'None' And '$(_LinkMode)' != 'SdkOnly' And '$(_LinkMode)' != 'Full'" />

Expand Down Expand Up @@ -370,9 +385,12 @@
InvariantGlobalization=$(InvariantGlobalization)
ItemsDirectory=$(_LinkerItemsDirectory)
IsSimulatorBuild=$(_SdkIsSimulator)
LibMonoLinkMode=$(_LibMonoLinkMode)
LibXamarinLinkMode=$(_LibXamarinLinkMode)
LinkMode=$(_LinkMode)
MarshalManagedExceptionMode=$(_MarshalManagedExceptionMode)
MarshalObjectiveCExceptionMode=$(_MarshalObjectiveCExceptionMode)
@(_MonoLibrary -> 'MonoLibrary=%(Identity)')
Optimize=$(_BundlerOptimize)
PartialStaticRegistrarLibrary=$(_LibPartialStaticRegistrar)
Platform=$(_PlatformName)
Expand Down Expand Up @@ -814,9 +832,7 @@
<ItemGroup>
<_XamarinMainLibraries Include="$(_XamarinNativeLibraryDirectory)/$(_LibXamarinName)" />
<!-- Link with the libraries shipped with the mono runtime pack -->
<_XamarinMainLibraries Include="@(_MonoLibrary)" Condition="'$(_PlatformName)' != 'macOS'" />
<!-- But don't link with any dylibs on macOS, because they might have library initializers which may fail - instead delay any failures until whatever feature they provide is needed -->
<_XamarinMainLibraries Include="@(_MonoLibrary)" Condition="'$(_PlatformName)' == 'macOS' And ('%(_MonoLibrary.Extension)' == '.a' Or '%(_MonoLibrary.Filename)' == 'libcoreclr')" />
<_XamarinMainLibraries Include="@(_MonoLibrary)" />
<!-- Link with static libraries from NativeReference items ((xc)frameworks are handled in the _ComputeFrameworkFilesToPublish target, and dynamic libraries are handled in the _ComputeDynamicLibrariesToPublish target) -->
<_XamarinMainLibraries Include="@(_FileNativeReference)" />

Expand Down Expand Up @@ -884,7 +900,7 @@
</_ComputeLinkModeDependsOn>
</PropertyGroup>

<Target Name="_ComputePublishLocation" DependsOnTargets="_GenerateBundleName;_ParseBundlerArguments">
<Target Name="_ComputePublishLocation" DependsOnTargets="_GenerateBundleName;_ParseBundlerArguments;_MonoSelectRuntimeComponents">
<PropertyGroup>
<_AssemblyPublishDir Condition="'$(_PlatformName)' == 'iOS' Or '$(_PlatformName)' == 'tvOS' Or '$(_PlatformName)' == 'watchOS'">$(MSBuildProjectDirectory)\$(_AppBundlePath)\</_AssemblyPublishDir>
<_AssemblyPublishDir Condition="'$(_PlatformName)' == 'macOS' Or '$(_PlatformName)' == 'MacCatalyst'">$(MSBuildProjectDirectory)\$(_AppBundlePath)\Contents\$(_CustomBundleName)\</_AssemblyPublishDir>
Expand Down Expand Up @@ -936,6 +952,17 @@
'%(ResolvedFileToPublish.NuGetPackageId)' == '$(_MonoNugetPackageId)'
"
/>

<!-- Remove any dylibs Mono told us not to link with -->
<ResolvedFileToPublish
Remove="@(_MonoRuntimeComponentDontLink -> '$(_MonoRuntimePackPath)/native/%(Identity)')"
Condition=" '%(ResolvedFileToPublish.AssetType)' == 'native' And
'%(ResolvedFileToPublish.RuntimeIdentifier)' == '$(RuntimeIdentifier)' And
'%(ResolvedFileToPublish.NuGetPackageId)' == '$(_MonoNugetPackageId)'
"
/>

<Warning Text="Removing mono components: @(_MonoRuntimeComponentDontLink -> '$(_MonoRuntimePackPath)/native/%(Identity)')" />
</ItemGroup>
</Target>

Expand Down
34 changes: 34 additions & 0 deletions runtime/runtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
bool xamarin_supports_dynamic_registration = true;
const char *xamarin_runtime_configuration_name = NULL;

#if DOTNET
enum XamarinNativeLinkMode xamarin_libmono_native_link_mode = XamarinNativeLinkModeStaticObject;
const char **xamarin_runtime_libraries = NULL;
#endif

/* Callbacks */

xamarin_setup_callback xamarin_setup = NULL;
Expand Down Expand Up @@ -2435,6 +2440,20 @@ -(void) xamarinSetFlags: (enum XamarinGCHandleFlags) flags;
xamarin_assertion_message ("Failed to initialize the VM");
}

static bool
xamarin_is_native_library (const char *libraryName)
{
if (xamarin_runtime_libraries == NULL)
return false;

for (int i = 0; xamarin_runtime_libraries [i] != NULL; i++) {
if (!strcmp (xamarin_runtime_libraries [i], libraryName))
return true;
}

return false;
}

void*
xamarin_pinvoke_override (const char *libraryName, const char *entrypointName)
{
Expand Down Expand Up @@ -2463,6 +2482,21 @@ -(void) xamarinSetFlags: (enum XamarinGCHandleFlags) flags;
}
#endif // defined (__i386__) || defined (__x86_64__) || defined (__arm64__)
#endif // !defined (CORECLR_RUNTIME)
} else if (xamarin_is_native_library (libraryName)) {
switch (xamarin_libmono_native_link_mode) {
case XamarinNativeLinkModeStaticObject:
// otherwise lookup the symbol in loaded memory, like __Internal does.
symbol = dlsym (RTLD_DEFAULT, entrypointName);
break;
case XamarinNativeLinkModeDynamicLibrary:
// if we're not linking statically, then don't do anything at all, let mono handle whatever needs to be done
return NULL;
case XamarinNativeLinkModeFramework:
default:
// handle this as "DynamicLibrary" for now - do nothing.
LOG (PRODUCT ": Unhandled libmono link mode: %i when looking up %s in %s", xamarin_libmono_native_link_mode, entrypointName, libraryName);
return NULL;
}
} else {
return NULL;
}
Expand Down
9 changes: 9 additions & 0 deletions runtime/xamarin/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ enum XamarinExceptionTypes : int {
XamarinExceptionTypes_System_OutOfMemoryException,
};

// Keep in sync with AssemblyBuildTarget in AssemblyBuildTarget.cs
enum XamarinNativeLinkMode : int {
XamarinNativeLinkModeStaticObject,
XamarinNativeLinkModeDynamicLibrary,
XamarinNativeLinkModeFramework,
};

extern bool mono_use_llvm; // this is defined inside mono

#if DEBUG
Expand All @@ -109,6 +116,8 @@ extern enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode;
extern enum XamarinLaunchMode xamarin_launch_mode;
extern bool xamarin_supports_dynamic_registration;
extern const char *xamarin_runtime_configuration_name;
extern enum XamarinNativeLinkMode xamarin_libmono_native_link_mode;
extern const char** xamarin_runtime_libraries;

typedef void (*xamarin_setup_callback) ();
typedef int (*xamarin_extension_main_callback) (int argc, char** argv);
Expand Down
10 changes: 1 addition & 9 deletions tests/dotnet/MySimpleApp/macOS/Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
TOP=../../../..

include $(TOP)/Make.config

build:
$(DOTNET6) build /bl *.csproj $(MSBUILD_VERBOSITY)

run:
$(DOTNET6) build /bl *.csproj $(MSBUILD_VERBOSITY) -t:Run
include ../shared.mk
25 changes: 24 additions & 1 deletion tools/common/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public partial class Application
public bool IsExtension;
public ApplePlatform Platform { get { return Driver.TargetFramework.Platform; } }

public List<string> MonoLibraries = new List<string> ();
public List<string> InterpretedAssemblies = new List<string> ();

// EnableMSym: only implemented for Xamarin.iOS
Expand Down Expand Up @@ -182,8 +183,12 @@ public string FrameworksDirectory {
}

// How Mono should be embedded into the app.
AssemblyBuildTarget? libmono_link_mode;
public AssemblyBuildTarget LibMonoLinkMode {
get {
if (libmono_link_mode.HasValue)
return libmono_link_mode.Value;

if (Platform == ApplePlatform.MacOSX) {
// This property was implemented for iOS, but might be re-used for macOS if desired after testing to verify it works as expected.
throw ErrorHelper.CreateError (99, Errors.MX0099, "LibMonoLinkMode isn't a valid operation for macOS apps.");
Expand All @@ -199,11 +204,18 @@ public AssemblyBuildTarget LibMonoLinkMode {
return AssemblyBuildTarget.StaticObject;
}
}
set {
libmono_link_mode = value;
}
}

// How libxamarin should be embedded into the app.
AssemblyBuildTarget? libxamarin_link_mode;
public AssemblyBuildTarget LibXamarinLinkMode {
get {
if (libxamarin_link_mode.HasValue)
return libxamarin_link_mode.Value;

if (Platform == ApplePlatform.MacOSX) {
// This property was implemented for iOS, but might be re-used for macOS if desired after testing to verify it works as expected.
throw ErrorHelper.CreateError (99, Errors.MX0099, "LibXamarinLinkMode isn't a valid operation for macOS apps.");
Expand All @@ -219,14 +231,25 @@ public AssemblyBuildTarget LibXamarinLinkMode {
return AssemblyBuildTarget.StaticObject;
}
}
set {
libxamarin_link_mode = value;
}
}

// How the generated libpinvoke library should be linked into the app.
public AssemblyBuildTarget LibPInvokesLinkMode => LibXamarinLinkMode;
// How the profiler library should be linked into the app.
public AssemblyBuildTarget LibProfilerLinkMode => OnlyStaticLibraries ? AssemblyBuildTarget.StaticObject : AssemblyBuildTarget.DynamicLibrary;

// How the libmononative library should be linked into the app.
public AssemblyBuildTarget LibMonoNativeLinkMode => HasDynamicLibraries ? AssemblyBuildTarget.DynamicLibrary : AssemblyBuildTarget.StaticObject;
public AssemblyBuildTarget LibMonoNativeLinkMode {
get {
// if there's a specific way libmono is being linked, use the same way.
if (libmono_link_mode.HasValue)
return libmono_link_mode.Value;
return HasDynamicLibraries ? AssemblyBuildTarget.DynamicLibrary: AssemblyBuildTarget.StaticObject;
}
}

// If all assemblies are compiled into static libraries.
public bool OnlyStaticLibraries {
Expand Down
44 changes: 21 additions & 23 deletions tools/common/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -772,31 +772,29 @@ void GenerateIOSMain (StringWriter sw, Abi abi)

if (app.MonoNativeMode != MonoNativeMode.None) {
#if NET
// Mono doesn't support dllmaps for Mac Catalyst / macOS in .NET for now:
// macOS: https://github.com/dotnet/runtime/issues/43204
// Mac Catalyst: https://github.com/dotnet/runtime/issues/48110
var addDllMap = App.Platform != ApplePlatform.MacCatalyst && app.Platform != ApplePlatform.MacOSX;
// Mono doesn't support dllmaps for Mac Catalyst / macOS in .NET, so we're using an alternative:
// the PINVOKE_OVERRIDE runtime option. Since we have to use it for Mac Catalyst + macOS, let's
// just use it everywhere to simplify code. This means that at runtime we need to know how we
// linked to mono, so store that in the xamarin_libmono_native_link_mode variable.
// Ref: https://github.com/dotnet/runtime/issues/43204 (macOS) https://github.com/dotnet/runtime/issues/48110 (Mac Catalyst)
sw.WriteLine ($"\txamarin_libmono_native_link_mode = XamarinNativeLinkMode{app.LibMonoNativeLinkMode};");
sw.WriteLine ($"\txamarin_runtime_libraries = [");
foreach (var lib in app.MonoLibraries)
sw.WriteLine ($"\t\t\"{lib}\",");
sw.WriteLine ($"\t\tNULL ];");
#else
var addDllMap = true;
#endif
if (addDllMap) {
string mono_native_lib;
if (app.LibMonoNativeLinkMode == AssemblyBuildTarget.StaticObject)
mono_native_lib = "__Internal";
else
mono_native_lib = app.GetLibNativeName () + ".dylib";
sw.WriteLine ();
#if NET
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"libSystem.Native\", NULL, \"{mono_native_lib}\", NULL);");
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"libSystem.Security.Cryptography.Native.Apple\", NULL, \"{mono_native_lib}\", NULL);");
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"libSystem.Net.Security.Native\", NULL, \"{mono_native_lib}\", NULL);");
#else
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Native\", NULL, \"{mono_native_lib}\", NULL);");
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Security.Cryptography.Native.Apple\", NULL, \"{mono_native_lib}\", NULL);");
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Net.Security.Native\", NULL, \"{mono_native_lib}\", NULL);");
#endif
sw.WriteLine ();
string mono_native_lib;
if (app.LibMonoNativeLinkMode == AssemblyBuildTarget.StaticObject) {
mono_native_lib = "__Internal";
} else {
mono_native_lib = app.GetLibNativeName () + ".dylib";
}
sw.WriteLine ();
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Native\", NULL, \"{mono_native_lib}\", NULL);");
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Security.Cryptography.Native.Apple\", NULL, \"{mono_native_lib}\", NULL);");
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Net.Security.Native\", NULL, \"{mono_native_lib}\", NULL);");
sw.WriteLine ();
#endif
}

if (app.EnableDebug)
Expand Down
29 changes: 29 additions & 0 deletions tools/dotnet-linker/LinkerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

namespace Xamarin.Linker {
public class LinkerConfiguration {
string LinkerFile;

public List<Abi> Abis;
public string AOTCompiler;
public string AOTOutputDirectory;
Expand Down Expand Up @@ -74,6 +76,8 @@ public static LinkerConfiguration GetInstance (LinkContext context, bool createI
if (!File.Exists (linker_file))
throw new FileNotFoundException ($"The custom linker file {linker_file} does not exist.");

LinkerFile = linker_file;

Profile = new BaseProfile (this);
DerivedLinkContext = new DerivedLinkContext { LinkerConfiguration = this, };
Application = new Application (this);
Expand Down Expand Up @@ -145,6 +149,12 @@ public static LinkerConfiguration GetInstance (LinkContext context, bool createI
case "IsSimulatorBuild":
IsSimulatorBuild = string.Equals ("true", value, StringComparison.OrdinalIgnoreCase);
break;
case "LibMonoLinkMode":
Application.LibMonoLinkMode = ParseLinkMode (value, key);
break;
case "LibXamarinLinkMode":
Application.LibXamarinLinkMode = ParseLinkMode (value, key);
break;
case "LinkMode":
if (!Enum.TryParse<LinkMode> (value, true, out var lm))
throw new InvalidOperationException ($"Unable to parse the {key} value: {value} in {linker_file}");
Expand All @@ -164,6 +174,9 @@ public static LinkerConfiguration GetInstance (LinkContext context, bool createI
Application.MarshalObjectiveCExceptions = mode;
}
break;
case "MonoLibrary":
Application.MonoLibraries.Add (value);
break;
case "Optimize":
user_optimize_flags = value;
break;
Expand Down Expand Up @@ -298,6 +311,19 @@ public static LinkerConfiguration GetInstance (LinkContext context, bool createI
Application.Initialize ();
}

AssemblyBuildTarget ParseLinkMode (string value, string variableName)
{
if (string.Equals (value, "dylib", StringComparison.OrdinalIgnoreCase)) {
return AssemblyBuildTarget.DynamicLibrary;
} else if (string.Equals (value, "static", StringComparison.OrdinalIgnoreCase)) {
return AssemblyBuildTarget.StaticObject;
} else if (string.Equals (value, "framework", StringComparison.OrdinalIgnoreCase)) {
return AssemblyBuildTarget.Framework;
}

throw new InvalidOperationException ($"Invalid {variableName} '{value}' in {LinkerFile}");
}

public void Write ()
{
if (Verbosity > 0) {
Expand All @@ -318,6 +344,9 @@ public void Write ()
Console.WriteLine ($" LinkMode: {LinkMode}");
Console.WriteLine ($" MarshalManagedExceptions: {Application.MarshalManagedExceptions} (IsDefault: {Application.IsDefaultMarshalManagedExceptionMode})");
Console.WriteLine ($" MarshalObjectiveCExceptions: {Application.MarshalObjectiveCExceptions}");
Console.WriteLine ($" {Application.MonoLibraries.Count} mono libraries:");
foreach (var lib in Application.MonoLibraries.OrderBy (v => v))
Console.WriteLine ($" {lib}");
Console.WriteLine ($" Optimize: {user_optimize_flags} => {Application.Optimizations}");
Console.WriteLine ($" PartialStaticRegistrarLibrary: {PartialStaticRegistrarLibrary}");
Console.WriteLine ($" Platform: {Platform}");
Expand Down

0 comments on commit 5975c8b

Please sign in to comment.