// 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.Binary; using System.Collections.Generic; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis { // There is small set of ObjectDataBuilder in at src/installer/managed/Microsoft.NET.HostModel/ObjectDataBuilder.cs // only for ResourceData.WriteResources public struct ObjectDataBuilder #if !READYTORUN : Internal.Runtime.ITargetBinaryWriter #endif { public ObjectDataBuilder(NodeFactory factory, bool relocsOnly) : this(factory.Target, relocsOnly) { } public ObjectDataBuilder(TargetDetails target, bool relocsOnly) { _target = target; _data = default(ArrayBuilder<byte>); _relocs = default(ArrayBuilder<Relocation>); Alignment = 1; _definedSymbols = default(ArrayBuilder<ISymbolDefinitionNode>); #if DEBUG _numReservations = 0; _checkAllSymbolDependenciesMustBeMarked = !relocsOnly; #endif } private TargetDetails _target; private ArrayBuilder<Relocation> _relocs; private ArrayBuilder<byte> _data; public int Alignment { get; private set; } private ArrayBuilder<ISymbolDefinitionNode> _definedSymbols; #if DEBUG private int _numReservations; private bool _checkAllSymbolDependenciesMustBeMarked; #endif public int CountBytes { get { return _data.Count; } } public int TargetPointerSize { get { return _target.PointerSize; } } /// <summary> /// Raise the alignment requirement of this object to <paramref name="align"/>. This has no effect /// if the alignment requirement is already larger than <paramref name="align"/>. /// </summary> public void RequireInitialAlignment(int align) { Alignment = Math.Max(align, Alignment); } /// <summary> /// Raise the alignment requirement of this object to the target pointer size. This has no effect /// if the alignment requirement is already larger than a pointer size. /// </summary> public void RequireInitialPointerAlignment() { RequireInitialAlignment(_target.PointerSize); } public void EmitByte(byte emit) { _data.Add(emit); } public void EmitShort(short emit) { BinaryPrimitives.WriteInt16LittleEndian(_data.AppendSpan(sizeof(short)), emit); } public void EmitUShort(ushort emit) { BinaryPrimitives.WriteUInt16LittleEndian(_data.AppendSpan(sizeof(ushort)), emit); } public void EmitInt(int emit) { BinaryPrimitives.WriteInt32LittleEndian(_data.AppendSpan(sizeof(int)), emit); } public void EmitUInt(uint emit) { BinaryPrimitives.WriteUInt32LittleEndian(_data.AppendSpan(sizeof(uint)), emit); } public void EmitLong(long emit) { BinaryPrimitives.WriteInt64LittleEndian(_data.AppendSpan(sizeof(long)), emit); } public void EmitNaturalInt(int emit) { if (_target.PointerSize == 8) { EmitLong(emit); } else { Debug.Assert(_target.PointerSize == 4); EmitInt(emit); } } public void EmitHalfNaturalInt(short emit) { if (_target.PointerSize == 8) { EmitInt(emit); } else { Debug.Assert(_target.PointerSize == 4); EmitShort(emit); } } public void EmitCompressedUInt(uint emit) { if (emit < 128) { EmitByte((byte)(emit * 2 + 0)); } else if (emit < 128 * 128) { EmitByte((byte)(emit * 4 + 1)); EmitByte((byte)(emit >> 6)); } else if (emit < 128 * 128 * 128) { EmitByte((byte)(emit * 8 + 3)); EmitByte((byte)(emit >> 5)); EmitByte((byte)(emit >> 13)); } else if (emit < 128 * 128 * 128 * 128) { EmitByte((byte)(emit * 16 + 7)); EmitByte((byte)(emit >> 4)); EmitByte((byte)(emit >> 12)); EmitByte((byte)(emit >> 20)); } else { EmitByte((byte)15); EmitInt((int)emit); } } public void EmitBytes(byte[] bytes) { _data.Append(bytes); } public void EmitBytes(byte[] bytes, int offset, int length) { _data.Append(bytes, offset, length); } internal void EmitBytes(ArrayBuilder<byte> bytes) { _data.Append(bytes); } public void EmitZeroPointer() { _data.ZeroExtend(_target.PointerSize); } public void EmitZeros(int numBytes) { _data.ZeroExtend(numBytes); } private Reservation GetReservationTicket(int size) { #if DEBUG _numReservations++; #endif Reservation ticket = (Reservation)_data.Count; _data.ZeroExtend(size); return ticket; } #pragma warning disable CA1822 // Mark members as static private int ReturnReservationTicket(Reservation reservation) #pragma warning restore CA1822 // Mark members as static { #if DEBUG Debug.Assert(_numReservations > 0); _numReservations--; #endif return (int)reservation; } public Reservation ReserveByte() { return GetReservationTicket(1); } public void EmitByte(Reservation reservation, byte emit) { int offset = ReturnReservationTicket(reservation); _data[offset] = emit; } public Reservation ReserveShort() { return GetReservationTicket(2); } public void EmitShort(Reservation reservation, short emit) { int offset = ReturnReservationTicket(reservation); BinaryPrimitives.WriteInt16LittleEndian(_data.AsSpan(offset), emit); } public Reservation ReserveInt() { return GetReservationTicket(4); } public void EmitInt(Reservation reservation, int emit) { int offset = ReturnReservationTicket(reservation); BinaryPrimitives.WriteInt32LittleEndian(_data.AsSpan(offset), emit); } public void EmitUInt(Reservation reservation, uint emit) { int offset = ReturnReservationTicket(reservation); BinaryPrimitives.WriteUInt32LittleEndian(_data.AsSpan(offset), emit); } public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0) { #if DEBUG if (_checkAllSymbolDependenciesMustBeMarked) { var node = symbol as ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<NodeFactory>; if (node != null) Debug.Assert(node.Marked); } #endif _relocs.Add(new Relocation(relocType, _data.Count, symbol)); // And add space for the reloc switch (relocType) { case RelocType.WASM_TABLE_INDEX_I32: case RelocType.WASM_TABLE_INDEX_REL_I32: case RelocType.IMAGE_REL_BASED_REL32: case RelocType.IMAGE_REL_BASED_RELPTR32: case RelocType.IMAGE_REL_BASED_ABSOLUTE: case RelocType.IMAGE_REL_BASED_HIGHLOW: case RelocType.IMAGE_REL_SECREL: case RelocType.IMAGE_REL_TLSGD: case RelocType.IMAGE_REL_TPOFF: case RelocType.IMAGE_REL_FILE_ABSOLUTE: case RelocType.IMAGE_REL_BASED_ADDR32NB: case RelocType.IMAGE_REL_SYMBOL_SIZE: EmitInt(delta); break; case RelocType.IMAGE_REL_BASED_DIR64: EmitLong(delta); break; case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: case RelocType.IMAGE_REL_BASED_THUMB_MOV32: case RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL: case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21: case RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12: case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12: case RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL: case RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12: case RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_LO12_NC: case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: case RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT: case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I: case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S: Debug.Assert(delta == 0); // Do not vacate space for this kind of relocation, because // the space is embedded in the instruction. break; case RelocType.IMAGE_REL_FILE_CHECKSUM_CALLBACK: EmitZeros(delta); break; default: throw new NotImplementedException(); } } public void EmitPointerReloc(ISymbolNode symbol, int delta = 0) { EmitReloc(symbol, (_target.PointerSize == 8) ? RelocType.IMAGE_REL_BASED_DIR64 : RelocType.IMAGE_REL_BASED_HIGHLOW, delta); } public void EmitChecksumReloc(IChecksumNode checksum) { EmitReloc(checksum, RelocType.IMAGE_REL_FILE_CHECKSUM_CALLBACK, checksum.ChecksumSize); } public ObjectNode.ObjectData ToObjectData() { #if DEBUG Debug.Assert(_numReservations == 0); #endif ObjectNode.ObjectData returnData = new ObjectNode.ObjectData(_data.ToArray(), _relocs.ToArray(), Alignment, _definedSymbols.ToArray()); return returnData; } public enum Reservation { } public void AddSymbol(ISymbolDefinitionNode node) { _definedSymbols.Add(node); } public void PadAlignment(int align) { Debug.Assert((align == 2) || (align == 4) || (align == 8) || (align == 16)); int misalignment = _data.Count & (align - 1); if (misalignment != 0) { EmitZeros(align - misalignment); } } } } |