// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using dnlib.IO;
using dnlib.PE;
namespace dnlib.DotNet.Writer {
///
/// Relocations directory
///
public sealed class RelocDirectory : IChunk {
readonly Machine machine;
readonly List allRelocRvas = new List();
readonly List> relocSections = new List>();
bool isReadOnly;
FileOffset offset;
RVA rva;
uint totalSize;
readonly struct RelocInfo {
public readonly IChunk Chunk;
public readonly uint OffsetOrRva;
public RelocInfo(IChunk chunk, uint offset) {
Chunk = chunk;
OffsetOrRva = offset;
}
}
///
public FileOffset FileOffset => offset;
///
public RVA RVA => rva;
internal bool NeedsRelocSection => allRelocRvas.Count != 0;
///
/// Constructor
///
/// Machine
public RelocDirectory(Machine machine) => this.machine = machine;
///
public void SetOffset(FileOffset offset, RVA rva) {
isReadOnly = true;
this.offset = offset;
this.rva = rva;
var allRvas = new List(allRelocRvas.Count);
foreach (var info in allRelocRvas) {
uint relocRva;
if (info.Chunk is not null)
relocRva = (uint)info.Chunk.RVA + info.OffsetOrRva;
else
relocRva = info.OffsetOrRva;
allRvas.Add(relocRva);
}
allRvas.Sort();
uint prevPage = uint.MaxValue;
List pageList = null;
foreach (var relocRva in allRvas) {
uint page = relocRva & ~0xFFFU;
if (page != prevPage) {
prevPage = page;
if (pageList is not null)
totalSize += (uint)(8 + ((pageList.Count + 1) & ~1) * 2);
pageList = new List();
relocSections.Add(pageList);
}
pageList.Add(relocRva);
}
if (pageList is not null)
totalSize += (uint)(8 + ((pageList.Count + 1) & ~1) * 2);
}
///
public uint GetFileLength() => totalSize;
///
public uint GetVirtualSize() => GetFileLength();
///
public uint CalculateAlignment() => 0;
///
public void WriteTo(DataWriter writer) {
bool is64bit = machine.Is64Bit();
// 3 = IMAGE_REL_BASED_HIGHLOW, A = IMAGE_REL_BASED_DIR64
uint relocType = is64bit ? 0xA000U : 0x3000;
foreach (var pageList in relocSections) {
writer.WriteUInt32(pageList[0] & ~0xFFFU);
writer.WriteUInt32((uint)(8 + ((pageList.Count + 1) & ~1) * 2));
foreach (var rva in pageList)
writer.WriteUInt16((ushort)(relocType | (rva & 0xFFF)));
if ((pageList.Count & 1) != 0)
writer.WriteUInt16(0);
}
}
///
/// Adds a relocation
///
/// RVA of location
public void Add(RVA rva) {
if (isReadOnly)
throw new InvalidOperationException("Can't add a relocation when the relocs section is read-only");
allRelocRvas.Add(new RelocInfo(null, (uint)rva));
}
///
/// Adds a relocation
///
/// Chunk or null. If it's null, is the RVA
/// Offset relative to the start of , or if is null, this is the RVA
public void Add(IChunk chunk, uint offset) {
if (isReadOnly)
throw new InvalidOperationException("Can't add a relocation when the relocs section is read-only");
allRelocRvas.Add(new RelocInfo(chunk, offset));
}
}
}