Skip to content

Commit

Permalink
ObjWriter in C# (#95876)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
filipnavara authored Jan 8, 2024
1 parent 21b4a85 commit abffa8f
Show file tree
Hide file tree
Showing 34 changed files with 11,429 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum SectionType
Writeable,
Executable,
Uninitialized,
Debug,
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public enum RelocType
// This is a special NGEN-specific relocation type
// for relative pointer (used to make NGen relocation
// section smaller)
IMAGE_REL_SECTION = 0x79, // 16 bit section index containing target

IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 = 0x81, // ADRP
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A = 0x82, // ADD/ADDS (immediate) with zero shift, for page offset
Expand Down Expand Up @@ -465,9 +466,13 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v
switch (relocType)
{
case RelocType.IMAGE_REL_BASED_ABSOLUTE:
case RelocType.IMAGE_REL_BASED_ADDR32NB:
case RelocType.IMAGE_REL_BASED_HIGHLOW:
case RelocType.IMAGE_REL_BASED_REL32:
case RelocType.IMAGE_REL_BASED_ADDR32NB:
case RelocType.IMAGE_REL_BASED_RELPTR32:
case RelocType.IMAGE_REL_SECREL:
case RelocType.IMAGE_REL_TLSGD:
case RelocType.IMAGE_REL_TPOFF:
case RelocType.IMAGE_REL_SYMBOL_SIZE:
case RelocType.IMAGE_REL_FILE_ABSOLUTE:
*(int*)location = (int)value;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

using static ILCompiler.ObjectWriter.CodeViewNative;

namespace ILCompiler.ObjectWriter
{
internal sealed class CodeViewFileTableBuilder
{
private readonly MemoryStream _stringTableWriter = new();
private readonly MemoryStream _fileTableWriter = new();
private readonly Dictionary<string, uint> _fileNameToIndex = new();

public CodeViewFileTableBuilder()
{
// Insert empty entry at the beginning of string table
_stringTableWriter.Write(stackalloc byte[2] { 0, 0 });
}

public uint GetFileIndex(string fileName)
{
if (fileName == "")
{
// Match the placeholder value from LLVM. We need to use a non-empty
// string to ensure that the null terminator of the UTF-8 representation
// is not treated as the terminator of the whole file name table.
fileName = "<stdin>";
}

if (_fileNameToIndex.TryGetValue(fileName, out uint fileIndex))
{
return fileIndex;
}
else
{
uint stringTableIndex = (uint)_stringTableWriter.Position;
_stringTableWriter.Write(Encoding.UTF8.GetBytes(fileName));
_stringTableWriter.WriteByte(0);

uint fileTableIndex = (uint)_fileTableWriter.Position;
Span<byte> fileTableEntry = stackalloc byte[sizeof(uint) + sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(fileTableEntry, stringTableIndex);
BinaryPrimitives.WriteUInt32LittleEndian(fileTableEntry.Slice(4), 0);
_fileTableWriter.Write(fileTableEntry);

_fileNameToIndex.Add(fileName, fileTableIndex);

return fileTableIndex;
}
}

public void Write(SectionWriter sectionWriter)
{
sectionWriter.WriteLittleEndian<uint>((uint)DebugSymbolsSubsectionType.FileChecksums);
sectionWriter.WriteLittleEndian<uint>((uint)_fileTableWriter.Length);
sectionWriter.EmitData(_fileTableWriter.GetBuffer().AsMemory(0, (int)_fileTableWriter.Length));
sectionWriter.WriteLittleEndian<uint>((uint)DebugSymbolsSubsectionType.StringTable);
sectionWriter.WriteLittleEndian<uint>((uint)_stringTableWriter.Length);
sectionWriter.EmitData(_stringTableWriter.GetBuffer().AsMemory(0, (int)_stringTableWriter.Length));
}
}
}

Large diffs are not rendered by default.

Loading

0 comments on commit abffa8f

Please sign in to comment.