diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc
index e37614b660c32f..b1a437d8b57ead 100644
--- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc
+++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc
@@ -33,7 +33,12 @@ C_FUNC(\Name):
.endm
.macro ALTERNATE_ENTRY Name
+#if defined(__APPLE__)
+ .alt_entry C_FUNC(\Name)
+ .private_extern C_FUNC(\Name)
+#else
.global C_FUNC(\Name)
+#endif
C_FUNC(\Name):
.endm
diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc
index fc19d451640f43..a02a770aa3b0af 100644
--- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc
+++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc
@@ -24,8 +24,11 @@ C_FUNC(\Name):
.endm
.macro ALTERNATE_ENTRY Name
+#if defined(__APPLE__)
+ .alt_entry C_FUNC(\Name)
+ .private_extern C_FUNC(\Name)
+#else
.global C_FUNC(\Name)
-#if !defined(__APPLE__)
.hidden C_FUNC(\Name)
#endif
C_FUNC(\Name):
diff --git a/src/coreclr/pal/inc/unixasmmacrosarm64.inc b/src/coreclr/pal/inc/unixasmmacrosarm64.inc
index 976cc825f2eb40..4997e18b39858a 100644
--- a/src/coreclr/pal/inc/unixasmmacrosarm64.inc
+++ b/src/coreclr/pal/inc/unixasmmacrosarm64.inc
@@ -17,7 +17,12 @@
.endm
.macro PATCH_LABEL Name
+#if defined(__APPLE__)
+ .alt_entry C_FUNC(\Name)
+ .private_extern C_FUNC(\Name)
+#else
.global C_FUNC(\Name)
+#endif
C_FUNC(\Name):
.endm
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs
index cecffda8bfe537..89dbcb463a2bf8 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs
@@ -43,6 +43,17 @@ public bool IsStandardSection
}
}
+ ///
+ /// Returns true if the section contains regular data (as opposed to code, debug info or unwinding data)
+ ///
+ public bool IsDataSection
+ {
+ get
+ {
+ return Type is SectionType.ReadOnly or SectionType.Writeable or SectionType.Uninitialized;
+ }
+ }
+
public static readonly ObjectNodeSection XDataSection = new ObjectNodeSection("xdata", SectionType.ReadOnly);
public static readonly ObjectNodeSection DataSection = new ObjectNodeSection("data", SectionType.Writeable);
public static readonly ObjectNodeSection ReadOnlyDataSection = new ObjectNodeSection("rdata", SectionType.ReadOnly);
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs
index 9c4f07c866cfc7..f9cda2cfc482a6 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs
@@ -96,6 +96,7 @@ internal static class MachNative
public const byte N_INDR = 0xA;
public const byte N_SECT = 0xE;
public const byte N_PBUD = 0xC;
+ public const byte N_PEXT = 0x10;
// Symbol descriptor flags
public const ushort REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0;
@@ -106,6 +107,7 @@ internal static class MachNative
public const ushort REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5;
public const ushort REFERENCED_DYNAMICALLY = 0x10;
public const ushort N_NO_DEAD_STRIP = 0x20;
+ public const ushort N_ALT_ENTRY = 0x200;
public const ushort N_WEAK_REF = 0x40;
public const ushort N_WEAK_DEF = 0x80;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs
index 3001640249d44b..3398489039b52b 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs
@@ -113,7 +113,7 @@ private protected override void EmitSectionsAndLayout()
Name = $"lsection{sectionIndex}",
Section = section,
Value = section.VirtualAddress,
- Descriptor = 0,
+ Descriptor = (ushort)(section.KeepDataLayout ? N_NO_DEAD_STRIP : 0),
Type = N_SECT,
};
_symbolTable.Add(machSymbol);
@@ -355,6 +355,7 @@ private protected override void CreateSection(ObjectNodeSection section, string
{
Log2Alignment = 1,
Flags = flags,
+ KeepDataLayout = section.IsDataSection
};
int sectionIndex = _sections.Count;
@@ -472,6 +473,10 @@ private protected override void EmitSymbolTable(
// Sort and insert all defined symbols
var sortedDefinedSymbols = new List(definedSymbols.Count);
+ // We emit a local N_NO_DEAD_STRIP symbol for the beginning of all sections marked with
+ // KeepDataLayout to ensure the final layout is kept intact by the linker. This works in
+ // tandem with setting the N_ALT_ENTRY flag for all other symbols in the same section in
+ // the loop below.
foreach ((string name, SymbolDefinition definition) in definedSymbols)
{
MachSection section = _sections[definition.SectionIndex];
@@ -483,8 +488,8 @@ private protected override void EmitSymbolTable(
Name = name,
Section = section,
Value = section.VirtualAddress + (ulong)definition.Value,
- Descriptor = N_NO_DEAD_STRIP,
- Type = N_SECT | N_EXT,
+ Descriptor = (ushort)(section.KeepDataLayout || definition.AltEntry ? N_ALT_ENTRY | N_NO_DEAD_STRIP : N_NO_DEAD_STRIP),
+ Type = (byte)(N_SECT | N_EXT | (definition.Global ? 0 : N_PEXT)),
});
}
sortedDefinedSymbols.Sort((symA, symB) => string.CompareOrdinal(symA.Name, symB.Name));
@@ -496,7 +501,7 @@ private protected override void EmitSymbolTable(
}
_dySymbolTable.ExternalSymbolsIndex = _dySymbolTable.LocalSymbolsCount;
- _dySymbolTable.ExternalSymbolsCount = (uint)definedSymbols.Count;
+ _dySymbolTable.ExternalSymbolsCount = (uint)sortedDefinedSymbols.Count;
uint savedSymbolIndex = symbolIndex;
foreach (string externSymbol in undefinedSymbols)
@@ -883,6 +888,7 @@ private sealed class MachSection
public bool IsInFile => Size > 0 && Type != S_ZEROFILL && Type != S_GB_ZEROFILL && Type != S_THREAD_LOCAL_ZEROFILL;
public bool IsDwarfSection { get; }
+ public bool KeepDataLayout { get; set; }
public IList Relocations => relocationCollection ??= new List();
public Stream Stream => dataStream;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs
index b3203d08b0d6fd..1549b8710ffea7 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs
@@ -18,7 +18,7 @@ namespace ILCompiler.ObjectWriter
{
public abstract class ObjectWriter
{
- private protected sealed record SymbolDefinition(int SectionIndex, long Value, int Size = 0, bool Global = false);
+ private protected sealed record SymbolDefinition(int SectionIndex, long Value, int Size = 0, bool Global = false, bool AltEntry = false);
private protected sealed record SymbolicRelocation(long Offset, RelocType Type, string SymbolName, long Addend = 0);
private protected sealed record BlockToRelocate(int SectionIndex, long Offset, byte[] Data, Relocation[] Relocations);
@@ -246,11 +246,12 @@ protected internal void EmitSymbolDefinition(
string symbolName,
long offset = 0,
int size = 0,
- bool global = false)
+ bool global = false,
+ bool altEntry = false)
{
_definedSymbols.Add(
symbolName,
- new SymbolDefinition(sectionIndex, offset, size, global));
+ new SymbolDefinition(sectionIndex, offset, size, global, altEntry));
}
///
@@ -414,14 +415,17 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection