From 0b9c22dd0cc48d7af758a9f3370473d408ae0bcf Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 21 Feb 2026 20:59:44 +0100 Subject: [PATCH 01/13] Implement a workaround for ld-prime hitting an assert on large addends We translate the IMAGE_REL_BASED_RELPTR32 relocation into ARM64_RELOC_SUBTRACTOR and ARM64_RELOC_UNSIGNED pair on ARM64. To emulate the behavior of PC relative relocation we bake the section-relative PC offset into the addend with negative sign. The ARM64_RELOC_SUBTRACTOR relocation then subtract the base address of the section and finally the ARM64_RELOC_UNSIGNED relocation adds the target symbol address. This works fine for addends that fit into signed 24-bit integer, but ld-prime hits an assert when the addend is larger. The workaround is to generate a temporary label at the offset of the relocated address and adjust the addend to be relative to that label. We reuse the last temporary label for following relocations in the same section until we hit another overflow. This way we minimize the number of temporary labels we need to generate while still satisfying the ld-prime requirement. Same logic applies to X86_64_RELOC_SUBTRACTOR + X86_64_RELOC_UNSIGNED pair for x64 targets. --- .../Compiler/ObjectWriter/MachObjectWriter.cs | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index c004d2b548b406..8f13c0ceed4b58 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -61,6 +61,8 @@ internal sealed partial class MachObjectWriter : UnixObjectWriter private readonly uint _cpuType; private readonly uint _cpuSubType; private readonly List _sections = new(); + private readonly List _temporaryLabels = new(); + private uint _temporaryLabelsBaseIndex; // Symbol table private readonly Dictionary _symbolNameToIndex = new(); @@ -482,10 +484,30 @@ protected internal override unsafe void EmitRelocation( // subtraction and the PC offset is baked into the addend. // On x64, ld64 requires X86_64_RELOC_SUBTRACTOR + X86_64_RELOC_UNSIGNED // for DWARF .eh_frame section. + + // Apple ld-prime linker needs a quirk where we restrict the addend to + // signed 24-bit integer. We generate temporary label every time the + // addend would overflow and adjust the addend to be relative to that label. + var temporaryLabelOffset = _sections[sectionIndex].TemporaryLabelOffset; + var temporaryLabelIndex = _sections[sectionIndex].TemporaryLabelIndex; + addend -= offset - temporaryLabelOffset; + if ((int)addend != (((int)addend << 8) >> 8)) + { + var oldAddend = addend; + addend += offset - temporaryLabelOffset; + temporaryLabelOffset = offset; + _temporaryLabels.Add(new SymbolDefinition(sectionIndex, temporaryLabelOffset, 0, false)); + _sections[sectionIndex].TemporaryLabelOffset = temporaryLabelOffset; + _sections[sectionIndex].TemporaryLabelIndex = (uint)_temporaryLabels.Count; + } + BinaryPrimitives.WriteInt32LittleEndian( data, BinaryPrimitives.ReadInt32LittleEndian(data) + - (int)(addend - offset)); + (int)addend); + + // Abuse the addend to store the temporary label index. + addend = temporaryLabelIndex; } else { @@ -497,8 +519,8 @@ protected internal override unsafe void EmitRelocation( BinaryPrimitives.ReadInt32LittleEndian(data) + (int)addend); } + addend = 0; } - addend = 0; break; default: @@ -524,6 +546,20 @@ private protected override void EmitSymbolTable( // We already emitted symbols for all non-debug sections in EmitSectionsAndLayout, // these symbols are local and we need to account for them. uint symbolIndex = (uint)_symbolTable.Count; + _temporaryLabelsBaseIndex = symbolIndex; + foreach (SymbolDefinition definition in _temporaryLabels) + { + MachSection section = _sections[definition.SectionIndex]; + _symbolTable.Add(new MachSymbol + { + Name = new Utf8StringBuilder().Append("Ltmp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), + Section = section, + Value = section.VirtualAddress + (ulong)definition.Value, + Descriptor = 0, + Type = N_SECT, + }); + symbolIndex++; + } _dySymbolTable.LocalSymbolsIndex = 0; _dySymbolTable.LocalSymbolsCount = symbolIndex; @@ -658,11 +694,18 @@ private void EmitRelocationsX64(int sectionIndex, List reloc } else if (symbolicRelocation.Type == IMAGE_REL_BASED_RELPTR32 && IsEhFrameSection(sectionIndex)) { + // Addend is used to encode the temporary label index for ld-prime quirk. See + // EmitRelocation for details. + uint baseSymbolIndex = + symbolicRelocation.Addend != 0 ? + (uint)(_temporaryLabelsBaseIndex + symbolicRelocation.Addend - 1) : + (uint)sectionIndex; + sectionRelocations.Add( new MachRelocation { Address = (int)symbolicRelocation.Offset, - SymbolOrSectionIndex = (uint)sectionIndex, + SymbolOrSectionIndex = baseSymbolIndex, Length = 4, RelocationType = X86_64_RELOC_SUBTRACTOR, IsExternal = true, @@ -821,12 +864,19 @@ private void EmitRelocationsArm64(int sectionIndex, List rel } else if (symbolicRelocation.Type == IMAGE_REL_BASED_RELPTR32) { + // Addend is used to encode the temporary label index for ld-prime quirk. See + // EmitRelocation for details. + uint baseSymbolIndex = + symbolicRelocation.Addend != 0 ? + (uint)(_temporaryLabelsBaseIndex + symbolicRelocation.Addend - 1) : + (uint)sectionIndex; + // This one is tough... needs to be represented by ARM64_RELOC_SUBTRACTOR + ARM64_RELOC_UNSIGNED. sectionRelocations.Add( new MachRelocation { Address = (int)symbolicRelocation.Offset, - SymbolOrSectionIndex = (uint)sectionIndex, + SymbolOrSectionIndex = baseSymbolIndex, Length = 4, RelocationType = ARM64_RELOC_SUBTRACTOR, IsExternal = true, @@ -975,6 +1025,9 @@ private sealed class MachSection public Stream Stream => dataStream; public byte SectionIndex { get; set; } + public long TemporaryLabelOffset { get; set; } + public uint TemporaryLabelIndex { get; set; } + public static int HeaderSize => 80; // 64-bit section public MachSection(string segmentName, string sectionName, Stream stream) From b99ee70b59793845de8e1158ba718f2ca3915f24 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 22 Feb 2026 11:38:54 +0100 Subject: [PATCH 02/13] Fix missing temporaryLabelIndex variable assignment --- .../Common/Compiler/ObjectWriter/MachObjectWriter.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index 8f13c0ceed4b58..2e24473ff62a1c 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -493,12 +493,10 @@ protected internal override unsafe void EmitRelocation( addend -= offset - temporaryLabelOffset; if ((int)addend != (((int)addend << 8) >> 8)) { - var oldAddend = addend; addend += offset - temporaryLabelOffset; - temporaryLabelOffset = offset; - _temporaryLabels.Add(new SymbolDefinition(sectionIndex, temporaryLabelOffset, 0, false)); - _sections[sectionIndex].TemporaryLabelOffset = temporaryLabelOffset; - _sections[sectionIndex].TemporaryLabelIndex = (uint)_temporaryLabels.Count; + _temporaryLabels.Add(new SymbolDefinition(sectionIndex, offset, 0, false)); + _sections[sectionIndex].TemporaryLabelOffset = temporaryLabelOffset = offset; + _sections[sectionIndex].TemporaryLabelIndex = temporaryLabelIndex = (uint)_temporaryLabels.Count; } BinaryPrimitives.WriteInt32LittleEndian( From 2fa03fa21734b5ab526abbad0897e0b7951f4163 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 22 Feb 2026 15:48:15 +0100 Subject: [PATCH 03/13] Prevent dead-striping of dehydrated data by the linker --- .../tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index 2e24473ff62a1c..ea2551c9eae338 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -550,10 +550,10 @@ private protected override void EmitSymbolTable( MachSection section = _sections[definition.SectionIndex]; _symbolTable.Add(new MachSymbol { - Name = new Utf8StringBuilder().Append("Ltmp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), + Name = new Utf8StringBuilder().Append("ltmp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), Section = section, Value = section.VirtualAddress + (ulong)definition.Value, - Descriptor = 0, + Descriptor = N_NO_DEAD_STRIP, Type = N_SECT, }); symbolIndex++; From c157abb5140be395272b8daf964f390ddf805bd3 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 22 Feb 2026 16:14:55 +0100 Subject: [PATCH 04/13] ltmp is special --- .../tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index ea2551c9eae338..d821de2d942d39 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -550,7 +550,7 @@ private protected override void EmitSymbolTable( MachSection section = _sections[definition.SectionIndex]; _symbolTable.Add(new MachSymbol { - Name = new Utf8StringBuilder().Append("ltmp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), + Name = new Utf8StringBuilder().Append("Ltmp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), Section = section, Value = section.VirtualAddress + (ulong)definition.Value, Descriptor = N_NO_DEAD_STRIP, From 4a18e9e0944f88e231f3d1f8d5c2d539d07e54b0 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 22 Feb 2026 20:53:07 +0100 Subject: [PATCH 05/13] Name the local label differently --- .../tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index d821de2d942d39..97daeb9395e8c4 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -550,10 +550,10 @@ private protected override void EmitSymbolTable( MachSection section = _sections[definition.SectionIndex]; _symbolTable.Add(new MachSymbol { - Name = new Utf8StringBuilder().Append("Ltmp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), + Name = new Utf8StringBuilder().Append("ltemp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), Section = section, Value = section.VirtualAddress + (ulong)definition.Value, - Descriptor = N_NO_DEAD_STRIP, + Descriptor = 0, Type = N_SECT, }); symbolIndex++; From be48d7262b478485bdeb6e5d31ba3e8993178c10 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 22 Feb 2026 23:20:54 +0100 Subject: [PATCH 06/13] Add N_NO_DEAD_STRIP to make Xcode 16 -ld_classic happy --- .../tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index 97daeb9395e8c4..8596186b04e1d2 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -553,7 +553,7 @@ private protected override void EmitSymbolTable( Name = new Utf8StringBuilder().Append("ltemp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), Section = section, Value = section.VirtualAddress + (ulong)definition.Value, - Descriptor = 0, + Descriptor = N_NO_DEAD_STRIP, Type = N_SECT, }); symbolIndex++; From 3f8dd48c4cfb79497dd1770ea8dc5482f4c0aa8f Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 22 Feb 2026 23:21:49 +0100 Subject: [PATCH 07/13] Be more conservative in the largest allowed addend --- .../tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index 8596186b04e1d2..eaef8de88af96f 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -486,12 +486,12 @@ protected internal override unsafe void EmitRelocation( // for DWARF .eh_frame section. // Apple ld-prime linker needs a quirk where we restrict the addend to - // signed 24-bit integer. We generate temporary label every time the + // signed 20-bit integer. We generate temporary label every time the // addend would overflow and adjust the addend to be relative to that label. var temporaryLabelOffset = _sections[sectionIndex].TemporaryLabelOffset; var temporaryLabelIndex = _sections[sectionIndex].TemporaryLabelIndex; addend -= offset - temporaryLabelOffset; - if ((int)addend != (((int)addend << 8) >> 8)) + if ((int)addend != (((int)addend << 12) >> 12)) { addend += offset - temporaryLabelOffset; _temporaryLabels.Add(new SymbolDefinition(sectionIndex, offset, 0, false)); From 610e0f180480af77b2cb158ce5528e5598ac12ee Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 23 Feb 2026 14:36:33 +0100 Subject: [PATCH 08/13] Test: Use Xcode 26.2 for build --- build.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.sh b/build.sh index 9df7f38ad7163f..9fc0daf527a5c7 100755 --- a/build.sh +++ b/build.sh @@ -11,6 +11,14 @@ function is_cygwin_or_mingw() esac } +# switch to different Xcode version +if [[ "$OSTYPE" == "darwin"* ]]; then + if [[ -d "/Applications/Xcode_26.2.app" ]]; then + echo "Switching to Xcode 26.2 for build" + sudo xcode-select -s "/Applications/Xcode_26.2.app" + fi +fi + # resolve $SOURCE until the file is no longer a symlink while [[ -h $source ]]; do scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" From 63cc8fd600224edd848a2fd7db0742a7e5ac9ccf Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 16 May 2026 13:56:27 +0200 Subject: [PATCH 09/13] Revert "Test: Use Xcode 26.2 for build" This reverts commit 610e0f180480af77b2cb158ce5528e5598ac12ee. --- build.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build.sh b/build.sh index 9fc0daf527a5c7..9df7f38ad7163f 100755 --- a/build.sh +++ b/build.sh @@ -11,14 +11,6 @@ function is_cygwin_or_mingw() esac } -# switch to different Xcode version -if [[ "$OSTYPE" == "darwin"* ]]; then - if [[ -d "/Applications/Xcode_26.2.app" ]]; then - echo "Switching to Xcode 26.2 for build" - sudo xcode-select -s "/Applications/Xcode_26.2.app" - fi -fi - # resolve $SOURCE until the file is no longer a symlink while [[ -h $source ]]; do scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" From 347293490770033a416d1148ebefe3417f95ce3f Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 16 May 2026 14:00:27 +0200 Subject: [PATCH 10/13] Add comment --- .../tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index eaef8de88af96f..071effc919ad21 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -1023,6 +1023,10 @@ private sealed class MachSection public Stream Stream => dataStream; public byte SectionIndex { get; set; } + // Variables used for tracking a temporary label created within the section + // for the purpose of creating relocations with smaller addends. The emitted + // addends are relative to the last temporary label instead of the section + // start. public long TemporaryLabelOffset { get; set; } public uint TemporaryLabelIndex { get; set; } From 67e24b55d90670b408a63df9afd4c68c99749091 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 16 May 2026 14:16:33 +0200 Subject: [PATCH 11/13] Address review comments: fix overflow check and add assertions - Replace (int) cast-based 20-bit overflow check with direct long comparison against signed 20-bit bounds [-524288, 524287] to avoid silent truncation for large addend values. - Add Debug.Assert before the int cast in WriteInt32LittleEndian to guard against unexpected out-of-range values. - Add Debug.Assert in EmitRelocationsX64/Arm64 to verify _temporaryLabelsBaseIndex has been initialized before use. - Improve comment on Addend repurposing to document the design choice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Compiler/ObjectWriter/MachObjectWriter.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index 071effc919ad21..80073603c72ef8 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -486,12 +486,12 @@ protected internal override unsafe void EmitRelocation( // for DWARF .eh_frame section. // Apple ld-prime linker needs a quirk where we restrict the addend to - // signed 20-bit integer. We generate temporary label every time the + // signed 20-bit integer. We generate a temporary label every time the // addend would overflow and adjust the addend to be relative to that label. var temporaryLabelOffset = _sections[sectionIndex].TemporaryLabelOffset; var temporaryLabelIndex = _sections[sectionIndex].TemporaryLabelIndex; addend -= offset - temporaryLabelOffset; - if ((int)addend != (((int)addend << 12) >> 12)) + if (addend != ((addend << 44) >> 44)) { addend += offset - temporaryLabelOffset; _temporaryLabels.Add(new SymbolDefinition(sectionIndex, offset, 0, false)); @@ -499,12 +499,17 @@ protected internal override unsafe void EmitRelocation( _sections[sectionIndex].TemporaryLabelIndex = temporaryLabelIndex = (uint)_temporaryLabels.Count; } + Debug.Assert(addend >= int.MinValue && addend <= int.MaxValue); BinaryPrimitives.WriteInt32LittleEndian( data, BinaryPrimitives.ReadInt32LittleEndian(data) + (int)addend); - // Abuse the addend to store the temporary label index. + // The SymbolicRelocation.Addend field is repurposed to carry the + // 1-based temporary label index (0 means no temporary label). This + // avoids introducing a side table and is consumed by + // EmitRelocationsX64/EmitRelocationsArm64 when building the + // subtractor relocation pair. addend = temporaryLabelIndex; } else @@ -694,6 +699,7 @@ private void EmitRelocationsX64(int sectionIndex, List reloc { // Addend is used to encode the temporary label index for ld-prime quirk. See // EmitRelocation for details. + Debug.Assert(symbolicRelocation.Addend == 0 || _temporaryLabelsBaseIndex > 0); uint baseSymbolIndex = symbolicRelocation.Addend != 0 ? (uint)(_temporaryLabelsBaseIndex + symbolicRelocation.Addend - 1) : @@ -864,6 +870,7 @@ private void EmitRelocationsArm64(int sectionIndex, List rel { // Addend is used to encode the temporary label index for ld-prime quirk. See // EmitRelocation for details. + Debug.Assert(symbolicRelocation.Addend == 0 || _temporaryLabelsBaseIndex > 0); uint baseSymbolIndex = symbolicRelocation.Addend != 0 ? (uint)(_temporaryLabelsBaseIndex + symbolicRelocation.Addend - 1) : From 6f6d5c01fe077e54748dc4cfb1eb1b8fe00bf306 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 16 May 2026 16:04:17 +0200 Subject: [PATCH 12/13] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Common/Compiler/ObjectWriter/MachObjectWriter.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index 80073603c72ef8..4f97af7d1ed764 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -550,12 +550,15 @@ private protected override void EmitSymbolTable( // these symbols are local and we need to account for them. uint symbolIndex = (uint)_symbolTable.Count; _temporaryLabelsBaseIndex = symbolIndex; + Utf8StringBuilder temporaryLabelNameBuilder = new Utf8StringBuilder(); foreach (SymbolDefinition definition in _temporaryLabels) { + temporaryLabelNameBuilder.Clear(); + temporaryLabelNameBuilder.Append("ltemp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)); MachSection section = _sections[definition.SectionIndex]; _symbolTable.Add(new MachSymbol { - Name = new Utf8StringBuilder().Append("ltemp"u8).Append((int)(symbolIndex - _temporaryLabelsBaseIndex)).ToUtf8String(), + Name = temporaryLabelNameBuilder.ToUtf8String(), Section = section, Value = section.VirtualAddress + (ulong)definition.Value, Descriptor = N_NO_DEAD_STRIP, @@ -699,11 +702,12 @@ private void EmitRelocationsX64(int sectionIndex, List reloc { // Addend is used to encode the temporary label index for ld-prime quirk. See // EmitRelocation for details. - Debug.Assert(symbolicRelocation.Addend == 0 || _temporaryLabelsBaseIndex > 0); + Debug.Assert(symbolicRelocation.Addend == 0 || symbolicRelocation.Addend <= _temporaryLabels.Count); uint baseSymbolIndex = symbolicRelocation.Addend != 0 ? (uint)(_temporaryLabelsBaseIndex + symbolicRelocation.Addend - 1) : (uint)sectionIndex; + Debug.Assert(baseSymbolIndex < (uint)_symbolTable.Count); sectionRelocations.Add( new MachRelocation From 84fc31e05a40df4da841e1855577e298c1af0a18 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 16 May 2026 16:31:08 +0200 Subject: [PATCH 13/13] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs index 4f97af7d1ed764..73554829b65ba6 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/MachObjectWriter.cs @@ -488,10 +488,12 @@ protected internal override unsafe void EmitRelocation( // Apple ld-prime linker needs a quirk where we restrict the addend to // signed 20-bit integer. We generate a temporary label every time the // addend would overflow and adjust the addend to be relative to that label. + const int signedAddendBitWidth = 20; + const int signedAddendShift = (sizeof(long) * 8) - signedAddendBitWidth; var temporaryLabelOffset = _sections[sectionIndex].TemporaryLabelOffset; var temporaryLabelIndex = _sections[sectionIndex].TemporaryLabelIndex; addend -= offset - temporaryLabelOffset; - if (addend != ((addend << 44) >> 44)) + if (addend != ((addend << signedAddendShift) >> signedAddendShift)) { addend += offset - temporaryLabelOffset; _temporaryLabels.Add(new SymbolDefinition(sectionIndex, offset, 0, false)); @@ -874,11 +876,12 @@ private void EmitRelocationsArm64(int sectionIndex, List rel { // Addend is used to encode the temporary label index for ld-prime quirk. See // EmitRelocation for details. - Debug.Assert(symbolicRelocation.Addend == 0 || _temporaryLabelsBaseIndex > 0); + Debug.Assert(symbolicRelocation.Addend == 0 || symbolicRelocation.Addend <= _temporaryLabels.Count); uint baseSymbolIndex = symbolicRelocation.Addend != 0 ? (uint)(_temporaryLabelsBaseIndex + symbolicRelocation.Addend - 1) : (uint)sectionIndex; + Debug.Assert(baseSymbolIndex < _symbolTable.Count); // This one is tough... needs to be represented by ARM64_RELOC_SUBTRACTOR + ARM64_RELOC_UNSIGNED. sectionRelocations.Add(