runtime: .NET 6 - R2R Composite image crashes the app
- .NET Core Version: 6.0.0-preview.4.21253.7
- Windows version: Windows 10 (Build 19043.1023)
- Does the bug reproduce also in WPF for .NET Framework 4.8?: No
- Is this bug related specifically to tooling in Visual Studio (e.g. XAML Designer, Code editing, etc…)? No.
Problem description:
When <PublishReadyToRunComposite>
is set to true, the WPF app would crash when trying to call something from System.Windows.Media.FontFamily
.
Actual behavior: The app crashes at startup.
Expected behavior: The app starts normally.
Minimal repro:
- Create a simple WPF app (with a bit of text and buttons).
- Add
<PublishReadyToRunComposite>
and<PublishReadyToRun>
to the csproj file and set it to true. dotnet publish -c Release -r win-x64
(may occur in win-x86, but I haven’t tested).
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 2
- Comments: 25 (16 by maintainers)
Commits related to this issue
- Fix WPF read to run https://github.com/dotnet/wpf/issues/4584 — committed to lindexi/lindexi_gd by lindexi 2 years ago
It still happens in .NET 7.
Publish Configuration: Self-contained win-x86 or win-x64 Produce single file => True Enable ReadyToRun compilation => True
Details: CoreCLR Version: 7.0.22.51805 .NET Version: 7.0.0 Description: The process was terminated due to an unhandled exception. Exception Info: System.TypeInitializationException: The type initializer for ‘System.Windows.Media.FontFamily’ threw an exception. —> System.TypeInitializationException: The type initializer for ‘MS.Internal.FontCache.DWriteFactory’ threw an exception. —> System.InvalidCastException: Specified cast is not valid. at MS.Internal.Text.TextInterface.Native.Util.ConvertHresultToException(Int32 hr) at MS.Internal.Text.TextInterface.Factory.Initialize(FactoryType factoryType) at MS.Internal.Text.TextInterface.Factory…ctor(FactoryType factoryType, IFontSourceCollectionFactory fontSourceCollectionFactory, IFontSourceFactory fontSourceFactory) at MS.Internal.FontCache.DWriteFactory…cctor() — End of inner exception stack trace — at MS.Internal.FontCache.DWriteFactory.get_SystemFontCollection() at System.Windows.Media.FontFamily…cctor() — End of inner exception stack trace — at MS.Internal.Text.DynamicPropertyReader.GetTypeface(DependencyObject element) at MS.Internal.Text.TextProperties.InitCommon(DependencyObject target) at MS.Internal.Text.TextProperties…ctor(FrameworkElement target, Boolean isTypographyDefaultValue) at System.Windows.Controls.TextBlock.GetLineProperties() at System.Windows.Controls.TextBlock.EnsureTextBlockCache() at System.Windows.Controls.TextBlock.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Controls.Border.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Controls.Control.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Controls.Grid.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Controls.Decorator.MeasureOverride(Size constraint) at System.Windows.Documents.AdornerDecorator.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Controls.Border.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Window.MeasureOverrideHelper(Size constraint) at System.Windows.Window.MeasureOverride(Size availableSize) at System.Windows.FrameworkElement.MeasureCore(Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Interop.HwndSource.SetLayoutSize() at System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value) at System.Windows.Window.SetRootVisualAndUpdateSTC() at System.Windows.Window.SetupInitialState(Double requestedTop, Double requestedLeft, Double requestedWidth, Double requestedHeight) at System.Windows.Window.CreateSourceWindow(Boolean duringShow) at System.Windows.Window.ShowHelper(Object booleanBox) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.DispatcherOperation.InvokeImpl() at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) — End of stack trace from previous location — at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state) at System.Windows.Threading.DispatcherOperation.Invoke() at System.Windows.Threading.Dispatcher.ProcessQueue() at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Application.RunDispatcher(Object ignore) at System.Windows.Application.RunInternal(Window window) at WpfApp2.App.Main()
Hello @dragnilar!
I believe I fixed the underlying issue with the PR
https://github.com/dotnet/runtime/issues/78723
that got merged in May i.o.w. it should be included in the latest .NET 8 previews (starting with Preview 5 I believe). It would be great if you or someone on this thread could confirm that the bug is indeed gone in .NET 8 so that we can close this issue.
Thanks
Tomas
I have realized there were several holes in my above reasoning so I investigated the matter further. The problem is not ordering of the RVA fields as I originally thought, that should be dealt with by means of
CopiedRvaFieldNode
comparison. The fundamental issue has to do with the core logic we use to access RVA fields. When R2R-compiling a single assembly, we potentially relocate RVA fields inCopiedMetadataBlob
but we always retain all of them. On the other hand, when compiling the R2R composite image, we selectively copy those RVA fields that get marked in the dependency analysis to the output image - while we use the correct order, we lose the individual initializer method pointers between__xc_ma_a
and__xc_ma_z
because they aren’t explicitly visible to the dependency analysis.In light of this fact I believe the problem is basically caused by incomplete design w.r.t. this corner case i.e. it requires additional design work, not a mere hotfix. In fact the only way to hotfix this would be to basically duplicate the RVA field file in the composite image, basically doubling its size in the R2R compilation, and I think that is wasteful and unnecessary. By virtue of the R2R design we always have the primary component assemblies available at runtime even in composite mode so we shouldn’t need to copy the RVA fields over to the composite image, we can (and should) access them directly in the primary component assembly metadata blobs.
I think this should be dealt with by means of a new RVA field fixup comprising the ECMA module and index of the RVA field in its metadata that would return the address of the field within the component assembly module via indirection into its RVA field metadata table. The JIT interface would then simply state that the address of the RVA field is accessible via this new fixup and it would no longer need to copy RVA fields to the composite image.
In non-composite build mode this ends up as a mere optimization where we directly know the RVA of the field based on the (copied) metadata blob. We could theoretically use the same optimization in composite mode but it would be more tricky as it would require the composite compilation to have knowledge of the process of rewriting the individual component assemblies so that it could use the rewritten RVA of the field in question.
For now I tend to think that is undesirable as otherwise rewriting of the component assemblies is completely orthogonal to composite compilation and can be potentially parallelized; I guess it is also somewhat questionable why we relocate the RVA fields at all during single-assembly compilation. From a broader perspective I would assume we should strive to mostly keep the original ECMA module intact and that includes the location of RVA fields (that can be potentially hard-coded in some native Managed C++ code although we don’t support that scenario just yet). If we fixed single-module compilation to stop tampering with RVA field addresses, the described optimization could be easily implemented in composite mode too.
I can confirm that the app still crashes. Even with
--self-contained
.Test.zip Here’s a small project for testing.
@acemod13: Could you share the entire .csproj? Thanks.