Dnspy/Extensions/dnSpy.AsmEditor/Compiler/MDEditor/DotNetMetadataSectionData.cs
2021-09-20 18:20:01 +02:00

109 lines
4.2 KiB
C#

/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace dnSpy.AsmEditor.Compiler.MDEditor {
sealed class DotNetMetadataSectionData : PESectionData {
public override uint Alignment => 8;
public uint RVA { get; private set; }
public uint Size { get; private set; }
readonly MetadataEditor mdEditor;
// PERF: Write as many rows as possible to this array, followed by a call to stream.Write(byte[]).
// This is much faster than writing one column at a time.
readonly byte[] buffer;
const int BUFFER_SIZE = 0x1000;
public DotNetMetadataSectionData(MetadataEditor mdEditor) {
this.mdEditor = mdEditor;
buffer = new byte[BUFFER_SIZE];
}
public override void Write(MDWriter mdWriter, uint rva, MDWriterStream stream) {
RVA = rva;
var startPos = stream.Position;
// PERF: We only write the known needed heaps. The #US heap isn't written since
// it's only used by method bodies and we don't write method bodies.
var heapWriters = new List<MDHeapWriter>(4);
heapWriters.Add(new TablesHeapWriter(mdEditor.TablesHeap, mdEditor.StringsHeap, mdEditor.GuidHeap, mdEditor.BlobHeap));
if (mdEditor.StringsHeap.ExistsInMetadata || mdEditor.StringsHeap.MustRewriteHeap())
heapWriters.Add(new StringsHeapWriter(mdEditor.StringsHeap));
if (mdEditor.GuidHeap.ExistsInMetadata || mdEditor.GuidHeap.MustRewriteHeap())
heapWriters.Add(new GuidHeapWriter(mdEditor.GuidHeap));
if (mdEditor.BlobHeap.ExistsInMetadata || mdEditor.BlobHeap.MustRewriteHeap())
heapWriters.Add(new BlobHeapWriter(mdEditor.BlobHeap));
var heapInfos = new(long offsetAndSizePosition, long dataPosition, long dataEndPosition)[heapWriters.Count];
// Write the metadata header
var mdHeader = mdWriter.MetadataEditor.RealMetadata.MetadataHeader;
stream.Write(0x424A5342);// BSJB
stream.Write(mdHeader.MajorVersion);
stream.Write(mdHeader.MinorVersion);
stream.Write(mdHeader.Reserved1);
var s = Encoding.UTF8.GetBytes(mdHeader.VersionString + "\0");
stream.Write(AlignUp((uint)s.Length, 4));
stream.Write(s);
stream.Position += AlignUp((uint)s.Length, 4) - s.Length;
stream.Write((byte)mdHeader.Flags);
stream.Write(mdHeader.Reserved2);
stream.Write((ushort)heapWriters.Count);
for (int i = 0; i < heapWriters.Count; i++) {
var heapWriter = heapWriters[i];
heapInfos[i].offsetAndSizePosition = stream.Position;
// We'll fix this later (offset + length)
stream.Position += 8;
stream.Write(s = Encoding.ASCII.GetBytes(heapWriter.Name + "\0"));
if (s.Length > 32)
throw new InvalidOperationException();
stream.Position += AlignUp((uint)s.Length, 4) - s.Length;
}
const uint HEAP_ALIGNMENT = 4;
for (int i = 0; i < heapWriters.Count; i++) {
var heapWriter = heapWriters[i];
stream.Position = (stream.Position + HEAP_ALIGNMENT - 1) & ~(HEAP_ALIGNMENT - 1);
heapInfos[i].dataPosition = stream.Position;
heapWriter.Write(mdWriter, stream, buffer);
heapInfos[i].dataEndPosition = stream.Position;
}
// Write the now known heap offsets and sizes
var currPos = stream.Position;
for (int i = 0; i < heapInfos.Length; i++) {
ref var info = ref heapInfos[i];
stream.Position = info.offsetAndSizePosition;
stream.Write((uint)(info.dataPosition - startPos));
stream.Write((uint)(info.dataEndPosition - info.dataPosition));
}
stream.Position = currPos;
Size = (uint)(stream.Position - startPos);
}
static uint AlignUp(uint offset, uint alignment) => (offset + alignment - 1) & ~(alignment - 1);
}
}