// dnlib: See LICENSE.txt for more info using System.Diagnostics; using dnlib.IO; using dnlib.PE; using dnlib.Protection; using dnlib.Utils; namespace dnlib.DotNet.Writer { /// /// Method body chunk /// public sealed class MethodBody : IChunk { const uint EXTRA_SECTIONS_ALIGNMENT = 4; readonly bool isTiny; readonly byte[] code; readonly byte[] extraSections; uint length; FileOffset offset; RVA rva; readonly uint localVarSigTok; /// public FileOffset FileOffset => offset; /// public RVA RVA => rva; /// /// Gets the code /// public byte[] Code => code; /// /// Gets the extra sections (exception handlers) or null /// public byte[] ExtraSections => extraSections; /// /// Gets the token of the locals /// public uint LocalVarSigTok => localVarSigTok; /// /// true if it's a fat body /// public bool IsFat => !isTiny; /// /// true if it's a tiny body /// public bool IsTiny => isTiny; /// /// true if there's an extra section /// public bool HasExtraSections => extraSections is not null && extraSections.Length > 0; /// /// Constructor /// /// Code public MethodBody(byte[] code) : this(code, null, 0) { } /// /// Constructor /// /// Code /// Extra sections or null public MethodBody(byte[] code, byte[] extraSections) : this(code, extraSections, 0) { } /// /// Constructor /// /// Code /// Extra sections or null /// Token of locals public MethodBody(byte[] code, byte[] extraSections, uint localVarSigTok) { isTiny = (code[0] & 3) == 2; this.code = code; this.extraSections = extraSections; this.localVarSigTok = localVarSigTok; } /// /// Gets the approximate size of the method body (code + exception handlers) /// public int GetApproximateSizeOfMethodBody() { int len = code.Length; if (extraSections is not null) { len = Utils.AlignUp(len, EXTRA_SECTIONS_ALIGNMENT); len += extraSections.Length; len = Utils.AlignUp(len, EXTRA_SECTIONS_ALIGNMENT); } return len; } internal bool CanReuse(RVA origRva, uint origSize) { uint length; if (HasExtraSections) { var rva2 = origRva + (uint)code.Length; rva2 = rva2.AlignUp(EXTRA_SECTIONS_ALIGNMENT); rva2 += (uint)extraSections.Length; length = (uint)rva2 - (uint)origRva; } else length = (uint)code.Length; return length <= origSize; } /// public void SetOffset(FileOffset offset, RVA rva) { Debug.Assert(this.rva == 0); this.offset = offset; this.rva = rva; if (HasExtraSections) { var rva2 = rva + (uint)code.Length; rva2 = rva2.AlignUp(EXTRA_SECTIONS_ALIGNMENT); rva2 += (uint)extraSections.Length; length = (uint)rva2 - (uint)rva; } else length = (uint)code.Length; } /// public uint GetFileLength() => length; /// public uint GetVirtualSize() => GetFileLength(); /// public uint CalculateAlignment() => 0; /// public void WriteTo(DataWriter writer) { writer.WriteBytes(code); //EncryptionUtil.WriteWitchEncIfNeed(writer, w => w.WriteBytes(code), e => e.MethodBodyEnc, EncryptionContext.SmallSegmentSize); if (HasExtraSections) { var rva2 = rva + (uint)code.Length; writer.WriteZeroes((int)rva2.AlignUp(EXTRA_SECTIONS_ALIGNMENT) - (int)rva2); writer.WriteBytes(extraSections); } } /// public override int GetHashCode() => Utils.GetHashCode(code) + Utils.GetHashCode(extraSections); /// public override bool Equals(object obj) { var other = obj as MethodBody; if (other is null) return false; return Utils.Equals(code, other.code) && Utils.Equals(extraSections, other.extraSections); } } }