Skip to content

Commit abffa8f

Browse files
authored
ObjWriter in C# (#95876)
This is reimplementation of NativeAOT ObjWriter in pure C# instead of depending on LLVM. It implements Mach-O, ELF, COFF object file emitter with DWARF and CodeView debugging information. Only x64 and arm64 targets are implemented to cover officially supported platforms (win-x86 code is present and lightly tested; linux-x86 code is present and incomplete, only serves as a test bed for emitting 32-bit ELF files if we ever need that). Original object writer code is still present and can be used by setting the `DOTNET_USE_LLVM_OBJWRITER=1` environment variable. **Thanks to @am11 for helping with testing and debugging this, @xoofx for making LibObjectFile which helped kickstart this project, @PaulusParssinen for tips about branchless U/SLEB128 size calculation, and all the people on the .NET team who helped push this forward!** Fixes #77178
1 parent 21b4a85 commit abffa8f

34 files changed

+11429
-27
lines changed

src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public enum SectionType
99
Writeable,
1010
Executable,
1111
Uninitialized,
12+
Debug,
1213
}
1314

1415
/// <summary>

src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public enum RelocType
2424
// This is a special NGEN-specific relocation type
2525
// for relative pointer (used to make NGen relocation
2626
// section smaller)
27+
IMAGE_REL_SECTION = 0x79, // 16 bit section index containing target
2728

2829
IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 = 0x81, // ADRP
2930
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A = 0x82, // ADD/ADDS (immediate) with zero shift, for page offset
@@ -465,9 +466,13 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v
465466
switch (relocType)
466467
{
467468
case RelocType.IMAGE_REL_BASED_ABSOLUTE:
469+
case RelocType.IMAGE_REL_BASED_ADDR32NB:
468470
case RelocType.IMAGE_REL_BASED_HIGHLOW:
469471
case RelocType.IMAGE_REL_BASED_REL32:
470-
case RelocType.IMAGE_REL_BASED_ADDR32NB:
472+
case RelocType.IMAGE_REL_BASED_RELPTR32:
473+
case RelocType.IMAGE_REL_SECREL:
474+
case RelocType.IMAGE_REL_TLSGD:
475+
case RelocType.IMAGE_REL_TPOFF:
471476
case RelocType.IMAGE_REL_SYMBOL_SIZE:
472477
case RelocType.IMAGE_REL_FILE_ABSOLUTE:
473478
*(int*)location = (int)value;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Buffers;
6+
using System.Buffers.Binary;
7+
using System.Collections.Generic;
8+
using System.Diagnostics;
9+
using System.IO;
10+
using System.Text;
11+
12+
using static ILCompiler.ObjectWriter.CodeViewNative;
13+
14+
namespace ILCompiler.ObjectWriter
15+
{
16+
internal sealed class CodeViewFileTableBuilder
17+
{
18+
private readonly MemoryStream _stringTableWriter = new();
19+
private readonly MemoryStream _fileTableWriter = new();
20+
private readonly Dictionary<string, uint> _fileNameToIndex = new();
21+
22+
public CodeViewFileTableBuilder()
23+
{
24+
// Insert empty entry at the beginning of string table
25+
_stringTableWriter.Write(stackalloc byte[2] { 0, 0 });
26+
}
27+
28+
public uint GetFileIndex(string fileName)
29+
{
30+
if (fileName == "")
31+
{
32+
// Match the placeholder value from LLVM. We need to use a non-empty
33+
// string to ensure that the null terminator of the UTF-8 representation
34+
// is not treated as the terminator of the whole file name table.
35+
fileName = "<stdin>";
36+
}
37+
38+
if (_fileNameToIndex.TryGetValue(fileName, out uint fileIndex))
39+
{
40+
return fileIndex;
41+
}
42+
else
43+
{
44+
uint stringTableIndex = (uint)_stringTableWriter.Position;
45+
_stringTableWriter.Write(Encoding.UTF8.GetBytes(fileName));
46+
_stringTableWriter.WriteByte(0);
47+
48+
uint fileTableIndex = (uint)_fileTableWriter.Position;
49+
Span<byte> fileTableEntry = stackalloc byte[sizeof(uint) + sizeof(uint)];
50+
BinaryPrimitives.WriteUInt32LittleEndian(fileTableEntry, stringTableIndex);
51+
BinaryPrimitives.WriteUInt32LittleEndian(fileTableEntry.Slice(4), 0);
52+
_fileTableWriter.Write(fileTableEntry);
53+
54+
_fileNameToIndex.Add(fileName, fileTableIndex);
55+
56+
return fileTableIndex;
57+
}
58+
}
59+
60+
public void Write(SectionWriter sectionWriter)
61+
{
62+
sectionWriter.WriteLittleEndian<uint>((uint)DebugSymbolsSubsectionType.FileChecksums);
63+
sectionWriter.WriteLittleEndian<uint>((uint)_fileTableWriter.Length);
64+
sectionWriter.EmitData(_fileTableWriter.GetBuffer().AsMemory(0, (int)_fileTableWriter.Length));
65+
sectionWriter.WriteLittleEndian<uint>((uint)DebugSymbolsSubsectionType.StringTable);
66+
sectionWriter.WriteLittleEndian<uint>((uint)_stringTableWriter.Length);
67+
sectionWriter.EmitData(_stringTableWriter.GetBuffer().AsMemory(0, (int)_stringTableWriter.Length));
68+
}
69+
}
70+
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewNative.cs

Lines changed: 2364 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)