diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 9b4377c7be7ed..044a731eb8684 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -9,6 +9,9 @@ typedef HINSTANCE (PALAPI_NOEXPORT *PFN_REGISTER_MODULE)(LPCSTR); /* u // This is for the PAL_VirtualUnwindOutOfProc read memory adapter. CrashInfo* g_crashInfo; +// This is the NativeAOT DotNetRuntimeDebugHeader signature +uint8_t g_debugHeaderCookie[4] = { 0x44, 0x4E, 0x44, 0x48 }; + static bool ModuleInfoCompare(const ModuleInfo* lhs, const ModuleInfo* rhs) { return lhs->BaseAddress() < rhs->BaseAddress(); } CrashInfo::CrashInfo(const CreateDumpOptions& options) : @@ -30,6 +33,7 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) : m_enumMemoryPagesAdded(0) { g_crashInfo = this; + m_runtimeBaseAddress = 0; #ifdef __APPLE__ m_task = 0; #else diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp index 90a31f106d684..89c71c9e586c8 100644 --- a/src/coreclr/debug/createdump/crashinfomac.cpp +++ b/src/coreclr/debug/createdump/crashinfomac.cpp @@ -3,6 +3,8 @@ #include "createdump.h" +extern uint8_t g_debugHeaderCookie[4]; + int g_readProcessMemoryResult = KERN_SUCCESS; bool @@ -263,6 +265,24 @@ void CrashInfo::VisitModule(MachOModule& module) } } } + else if (m_appModel == AppModelType::NativeAOT) + { + uint64_t symbolOffset; + if (module.TryLookupSymbol("DotNetRuntimeDebugHeader", &symbolOffset)) + { + m_coreclrPath = GetDirectory(module.Name()); + m_runtimeBaseAddress = module.BaseAddress(); + + uint8_t cookie[sizeof(g_debugHeaderCookie)]; + if (ReadMemory((void*)(module.BaseAddress() + symbolOffset), cookie, sizeof(cookie))) + { + if (memcmp(cookie, g_debugHeaderCookie, sizeof(g_debugHeaderCookie)) == 0) + { + TRACE("Found valid NativeAOT runtime module\n"); + } + } + } + } } // VisitSegment is called for each segment of the module module.EnumerateSegments(); diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index 583dad75a17ef..4d39ade7fda90 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -8,6 +8,7 @@ #endif extern CrashInfo* g_crashInfo; +extern uint8_t g_debugHeaderCookie[4]; int g_readProcessMemoryErrno = 0; @@ -383,6 +384,27 @@ CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName) } } } + else if (m_appModel == AppModelType::NativeAOT) + { + if (PopulateForSymbolLookup(baseAddress)) + { + uint64_t symbolOffset; + if (TryLookupSymbol("DotNetRuntimeDebugHeader", &symbolOffset)) + { + m_coreclrPath = GetDirectory(moduleName); + m_runtimeBaseAddress = baseAddress; + + uint8_t cookie[sizeof(g_debugHeaderCookie)]; + if (ReadMemory((void*)(baseAddress + symbolOffset), cookie, sizeof(cookie))) + { + if (memcmp(cookie, g_debugHeaderCookie, sizeof(g_debugHeaderCookie)) == 0) + { + TRACE("Found valid NativeAOT runtime module\n"); + } + } + } + } + } } EnumerateProgramHeaders(baseAddress); } diff --git a/src/coreclr/debug/createdump/dumpwriter.cpp b/src/coreclr/debug/createdump/dumpwriter.cpp index 5153ba790154f..0e39b52b3a7d4 100644 --- a/src/coreclr/debug/createdump/dumpwriter.cpp +++ b/src/coreclr/debug/createdump/dumpwriter.cpp @@ -39,7 +39,8 @@ DumpWriter::WriteDiagInfo(size_t size) SpecialDiagInfoHeader header = { {SPECIAL_DIAGINFO_SIGNATURE}, SPECIAL_DIAGINFO_VERSION, - m_crashInfo.ExceptionRecord() + m_crashInfo.ExceptionRecord(), + m_crashInfo.RuntimeBaseAddress() }; if (!WriteData(&header, sizeof(header))) { return false; diff --git a/src/coreclr/debug/createdump/specialdiaginfo.h b/src/coreclr/debug/createdump/specialdiaginfo.h index a857129c9c91f..84f79f00a160d 100644 --- a/src/coreclr/debug/createdump/specialdiaginfo.h +++ b/src/coreclr/debug/createdump/specialdiaginfo.h @@ -8,11 +8,11 @@ // ****************************************************************************** // This is a special memory region added to ELF and MachO dumps that contains extra diagnostics -// information like the exception record for a crash for a NativeAOT app. The exception record -// contains the pointer to the JSON formatted crash info. +// information like the exception record address for a NativeAOT app crash or the runtime module +// base address. The exception record contains the pointer to the JSON formatted crash info. #define SPECIAL_DIAGINFO_SIGNATURE "DIAGINFOHEADER" -#define SPECIAL_DIAGINFO_VERSION 1 +#define SPECIAL_DIAGINFO_VERSION 2 #ifdef __APPLE__ const uint64_t SpecialDiagInfoAddress = 0x7fffffff10000000; @@ -31,4 +31,5 @@ struct SpecialDiagInfoHeader char Signature[16]; int32_t Version; uint64_t ExceptionRecordAddress; + uint64_t RuntimeBaseAddress; }; diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index ba1e1480ed8e6..c496ce5b6699e 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -81,7 +81,7 @@ The .NET Foundation licenses this file to you under the MIT license. $(NativeIntermediateOutputPath)$(TargetName)$(NativeObjectExt) $(NativeOutputPath)$(TargetName)$(NativeBinaryExt) true - $(NativeIntermediateOutputPath)$(TargetName)$(ExportsFileExt) + $(NativeIntermediateOutputPath)$(TargetName)$(ExportsFileExt) $(NativeObject) @@ -240,7 +240,10 @@ The .NET Foundation licenses this file to you under the MIT license. - + + + + @@ -329,12 +332,11 @@ The .NET Foundation licenses this file to you under the MIT license. - - - + + diff --git a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp index b01339f812031..d6f34b10708e5 100644 --- a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp +++ b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp @@ -36,12 +36,12 @@ struct GlobalValueEntry // This size should be one bigger than the number of entries since a null entry // signifies the end of the array. -static constexpr size_t DebugTypeEntriesArraySize = 96; +static constexpr size_t DebugTypeEntriesArraySize = 100; static DebugTypeEntry s_DebugEntries[DebugTypeEntriesArraySize]; // This size should be one bigger than the number of entries since a null entry // signifies the end of the array. -static constexpr size_t GlobalEntriesArraySize = 6; +static constexpr size_t GlobalEntriesArraySize = 8; static GlobalValueEntry s_GlobalEntries[GlobalEntriesArraySize]; // This structure is part of a in-memory serialization format that is used by diagnostic tools to @@ -108,6 +108,10 @@ struct DotNetRuntimeDebugHeader }; extern "C" struct DotNetRuntimeDebugHeader DotNetRuntimeDebugHeader; + +#ifdef HOST_UNIX +__attribute__ ((visibility ("default"))) +#endif struct DotNetRuntimeDebugHeader DotNetRuntimeDebugHeader = {}; #define MAKE_DEBUG_ENTRY(TypeName, FieldName, Value) \ @@ -115,7 +119,7 @@ struct DotNetRuntimeDebugHeader DotNetRuntimeDebugHeader = {}; { \ s_DebugEntries[currentDebugPos] = { #TypeName, #FieldName, Value, 0 }; \ ++currentDebugPos; \ - ASSERT(currentDebugPos <= DebugTypeEntriesArraySize); \ + ASSERT(currentDebugPos < DebugTypeEntriesArraySize); \ } while(0) #define MAKE_DEBUG_FIELD_ENTRY(TypeName, FieldName) MAKE_DEBUG_ENTRY(TypeName, FieldName, offsetof(TypeName, FieldName)) @@ -129,7 +133,7 @@ struct DotNetRuntimeDebugHeader DotNetRuntimeDebugHeader = {}; { \ s_GlobalEntries[currentGlobalPos] = { #Name, Name }; \ ++currentGlobalPos; \ - ASSERT(currentGlobalPos <= GlobalEntriesArraySize); \ + ASSERT(currentGlobalPos < GlobalEntriesArraySize); \ } while(0) \ extern "C" void PopulateDebugHeaders() diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs index 9937ecacc986c..7f1a4e2b51de4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs @@ -12,13 +12,15 @@ namespace ILCompiler { public class ExportsFileWriter { - private string _exportsFile; - private List _methods; - private TypeSystemContext _context; + private readonly string _exportsFile; + private readonly IEnumerable _exportSymbols; + private readonly List _methods; + private readonly TypeSystemContext _context; - public ExportsFileWriter(TypeSystemContext context, string exportsFile) + public ExportsFileWriter(TypeSystemContext context, string exportsFile, IEnumerable exportSymbols) { _exportsFile = exportsFile; + _exportSymbols = exportSymbols; _context = context; _methods = new List(); } @@ -34,11 +36,15 @@ public void EmitExportedMethods() if (_context.Target.IsWindows) { streamWriter.WriteLine("EXPORTS"); + foreach (string symbol in _exportSymbols) + streamWriter.WriteLine($" {symbol.Replace(',', ' ')}"); foreach (var method in _methods) streamWriter.WriteLine($" {method.GetUnmanagedCallersOnlyExportName()}"); } else if(_context.Target.IsOSXLike) { + foreach (string symbol in _exportSymbols) + streamWriter.WriteLine($"_{symbol}"); foreach (var method in _methods) streamWriter.WriteLine($"_{method.GetUnmanagedCallersOnlyExportName()}"); } @@ -46,6 +52,8 @@ public void EmitExportedMethods() { streamWriter.WriteLine("V1.0 {"); streamWriter.WriteLine(" global: _init; _fini;"); + foreach (string symbol in _exportSymbols) + streamWriter.WriteLine($" {symbol};"); foreach (var method in _methods) streamWriter.WriteLine($" {method.GetUnmanagedCallersOnlyExportName()};"); streamWriter.WriteLine(" local: *;"); diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 4524f7a854bc5..d096cc9bbeb12 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -38,7 +38,11 @@ internal sealed class ILCompilerRootCommand : CliRootCommand public CliOption SplitExeInitialization { get; } = new("--splitinit") { Description = "Split initialization of an executable between the library entrypoint and a main entrypoint" }; public CliOption ExportsFile { get; } = - new("--exportsfile") { Description = "File to write exported method definitions" }; + new("--exportsfile") { Description = "File to write exported symbol and method definitions" }; + public CliOption ExportUnmanagedEntryPoints { get; } = + new("--export-unmanaged-entrypoints") { Description = "Controls whether the named UnmanagedCallersOnly methods are exported" }; + public CliOption ExportDynamicSymbols { get; } = + new("--export-dynamic-symbol") { Description = "Add dynamic export symbol to exports file" }; public CliOption DgmlLogFileName { get; } = new("--dgmllog") { Description = "Save result of dependency analysis as DGML" }; public CliOption GenerateFullDgmlLog { get; } = @@ -176,6 +180,8 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") Options.Add(NativeLib); Options.Add(SplitExeInitialization); Options.Add(ExportsFile); + Options.Add(ExportDynamicSymbols); + Options.Add(ExportUnmanagedEntryPoints); Options.Add(DgmlLogFileName); Options.Add(GenerateFullDgmlLog); Options.Add(ScanDgmlLogFileName); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 66df76365d8d2..0ff5cd87b5ffe 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -574,11 +574,15 @@ void RunScanner() string exportsFile = Get(_command.ExportsFile); if (exportsFile != null) { - ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile); - foreach (var compilationRoot in compilationRoots) + ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile, Get(_command.ExportDynamicSymbols)); + + if (Get(_command.ExportUnmanagedEntryPoints)) { - if (compilationRoot is UnmanagedEntryPointsRootProvider provider) - defFileWriter.AddExportedMethods(provider.ExportedMethods); + foreach (var compilationRoot in compilationRoots) + { + if (compilationRoot is UnmanagedEntryPointsRootProvider provider) + defFileWriter.AddExportedMethods(provider.ExportedMethods); + } } defFileWriter.EmitExportedMethods();