/// <summary> /// Serialize the export symbol table into the export section. /// </summary> /// <param name="location">RVA and file location of the .edata section</param> private BlobBuilder SerializeExportSection(SectionLocation sectionLocation) { _exportSymbols.MergeSort((es1, es2) => StringComparer.Ordinal.Compare(es1.Name, es2.Name)); BlobBuilder builder = new BlobBuilder(); int minOrdinal = int.MaxValue; int maxOrdinal = int.MinValue; // First, emit the name table and store the name RVA's for the individual export symbols // Also, record the ordinal range. foreach (ExportSymbol symbol in _exportSymbols) { symbol.NameRVAWhenPlaced = sectionLocation.RelativeVirtualAddress + builder.Count; builder.WriteUTF8(symbol.Name); builder.WriteByte(0); if (symbol.Ordinal < minOrdinal) { minOrdinal = symbol.Ordinal; } if (symbol.Ordinal > maxOrdinal) { maxOrdinal = symbol.Ordinal; } } // Emit the DLL name int dllNameRVA = sectionLocation.RelativeVirtualAddress + builder.Count; builder.WriteUTF8(_dllNameForExportDirectoryTable); builder.WriteByte(0); int[] addressTable = new int[maxOrdinal - minOrdinal + 1]; // Emit the name pointer table; it should be alphabetically sorted. // Also, we can now fill in the export address table as we've detected its size // in the previous pass. builder.Align(4); int namePointerTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (ExportSymbol symbol in _exportSymbols) { builder.WriteInt32(symbol.NameRVAWhenPlaced); SymbolTarget symbolTarget = _symbolMap[symbol.Symbol]; Section symbolSection = _sections[symbolTarget.SectionIndex]; Debug.Assert(symbolSection.RVAWhenPlaced != 0); addressTable[symbol.Ordinal - minOrdinal] = symbolSection.RVAWhenPlaced + symbolTarget.Offset; } // Emit the ordinal table int ordinalTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (ExportSymbol symbol in _exportSymbols) { builder.WriteUInt16((ushort)(symbol.Ordinal - minOrdinal)); } // Emit the address table builder.Align(4); int addressTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (int addressTableEntry in addressTable) { builder.WriteInt32(addressTableEntry); } // Emit the export directory table builder.Align(4); int exportDirectoryTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; // +0x00: reserved builder.WriteInt32(0); // +0x04: TODO: time/date stamp builder.WriteInt32(0); // +0x08: major version builder.WriteInt16(0); // +0x0A: minor version builder.WriteInt16(0); // +0x0C: DLL name RVA builder.WriteInt32(dllNameRVA); // +0x10: ordinal base builder.WriteInt32(minOrdinal); // +0x14: number of entries in the address table builder.WriteInt32(addressTable.Length); // +0x18: number of name pointers builder.WriteInt32(_exportSymbols.Count); // +0x1C: export address table RVA builder.WriteInt32(addressTableRVA); // +0x20: name pointer RVV builder.WriteInt32(namePointerTableRVA); // +0x24: ordinal table RVA builder.WriteInt32(ordinalTableRVA); int exportDirectorySize = sectionLocation.RelativeVirtualAddress + builder.Count - exportDirectoryTableRVA; _exportDirectoryEntry = new DirectoryEntry(relativeVirtualAddress: exportDirectoryTableRVA, size: exportDirectorySize); return(builder); }
/// <summary> /// Emit the .reloc section based on file relocation information in the individual blocks. /// We rely on the fact that the .reloc section is emitted last so that, by the time /// it's getting serialized, all other sections that may contain relocations have already /// been laid out. /// </summary> private BlobBuilder SerializeRelocationSection(SectionLocation sectionLocation) { // There are 12 bits for the relative offset const int RelocationTypeShift = 12; const int MaxRelativeOffsetInBlock = (1 << RelocationTypeShift) - 1; // Even though the format doesn't dictate it, it seems customary // to align the base RVA's on 4K boundaries. const int BaseRVAAlignment = 1 << RelocationTypeShift; BlobBuilder builder = new BlobBuilder(); int baseRVA = 0; List <ushort> offsetsAndTypes = null; Section relocSection = FindSection(R2RPEBuilder.RelocSectionName); if (relocSection != null) { relocSection.FilePosWhenPlaced = sectionLocation.PointerToRawData; relocSection.RVAWhenPlaced = sectionLocation.RelativeVirtualAddress; builder = relocSection.Content; } // Traverse relocations in all sections in their RVA order // By now, all "normal" sections with relocations should already have been laid out foreach (Section section in _sections.OrderBy((sec) => sec.RVAWhenPlaced)) { foreach (PlacedObjectData placedObjectData in section.PlacedObjectDataToRelocate) { for (int relocIndex = 0; relocIndex < placedObjectData.Relocs.Length; relocIndex++) { RelocType relocType = placedObjectData.Relocs[relocIndex].RelocType; RelocType fileRelocType = Relocation.GetFileRelocationType(relocType); if (fileRelocType != RelocType.IMAGE_REL_BASED_ABSOLUTE) { int relocationRVA = section.RVAWhenPlaced + placedObjectData.Offset + placedObjectData.Relocs[relocIndex].Offset; if (offsetsAndTypes != null && relocationRVA - baseRVA > MaxRelativeOffsetInBlock) { // Need to flush relocation block as the current RVA is too far from base RVA FlushRelocationBlock(builder, baseRVA, offsetsAndTypes); offsetsAndTypes = null; } if (offsetsAndTypes == null) { // Create new relocation block baseRVA = relocationRVA & -BaseRVAAlignment; offsetsAndTypes = new List <ushort>(); } ushort offsetAndType = (ushort)(((ushort)fileRelocType << RelocationTypeShift) | (relocationRVA - baseRVA)); offsetsAndTypes.Add(offsetAndType); } } } } if (offsetsAndTypes != null) { FlushRelocationBlock(builder, baseRVA, offsetsAndTypes); } if (builder.Count != 0) { _relocationDirectoryEntry = new DirectoryEntry(sectionLocation.RelativeVirtualAddress, builder.Count); } return(builder); }
public void AddSection(Section section) { _sections.Add(section); }
/// <summary> /// Add an ObjectData block to a given section. /// </summary> /// <param name="data">Block to add</param> /// <param name="sectionIndex">Section index</param> /// <param name="name">Node name to emit in the map file</param> /// <param name="outputInfoBuilder">Optional output info to collect (used for creating maps and symbols)</param> public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, string name, OutputInfoBuilder outputInfoBuilder) { Section section = _sections[sectionIndex]; // Calculate alignment padding - apparently ObjectDataBuilder can produce an alignment of 0 int alignedOffset = section.Content.Count; if (objectData.Alignment > 1) { alignedOffset = (section.Content.Count + objectData.Alignment - 1) & -objectData.Alignment; int padding = alignedOffset - section.Content.Count; if (padding > 0) { if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0) { uint cp = _codePadding; while (padding >= sizeof(uint)) { section.Content.WriteUInt32(cp); padding -= sizeof(uint); } if (padding >= 2) { section.Content.WriteUInt16(unchecked ((ushort)cp)); cp >>= 16; } if ((padding & 1) != 0) { section.Content.WriteByte(unchecked ((byte)cp)); } } else { section.Content.WriteBytes(0, padding); } } } if (outputInfoBuilder != null) { var node = new OutputNode(sectionIndex, alignedOffset, objectData.Data.Length, name); outputInfoBuilder.AddNode(node, objectData.DefinedSymbols[0]); if (objectData.Relocs != null) { foreach (Relocation reloc in objectData.Relocs) { RelocType fileReloc = Relocation.GetFileRelocationType(reloc.RelocType); if (fileReloc != RelocType.IMAGE_REL_BASED_ABSOLUTE) { outputInfoBuilder.AddRelocation(node, fileReloc); } } } } section.Content.WriteBytes(objectData.Data); if (objectData.DefinedSymbols != null) { foreach (ISymbolDefinitionNode symbol in objectData.DefinedSymbols) { if (outputInfoBuilder != null) { Utf8StringBuilder sb = new Utf8StringBuilder(); symbol.AppendMangledName(GetNameMangler(), sb); int sectionRelativeOffset = alignedOffset + symbol.Offset; outputInfoBuilder.AddSymbol(new OutputSymbol(sectionIndex, sectionRelativeOffset, sb.ToString())); } _symbolMap.Add(symbol, new SymbolTarget( sectionIndex: sectionIndex, offset: alignedOffset + symbol.Offset, size: objectData.Data.Length)); } } if (objectData.Relocs != null && objectData.Relocs.Length != 0) { section.PlacedObjectDataToRelocate.Add(new PlacedObjectData(alignedOffset, objectData)); } }
/// <summary> /// Add an ObjectData block to a given section. /// </summary> /// <param name="data">Block to add</param> /// <param name="sectionIndex">Section index</param> /// <param name="name">Node name to emit in the map file</param> /// <param name="mapFile">Optional map file to emit</param> public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, string name, TextWriter mapFile) { Section section = _sections[sectionIndex]; // Calculate alignment padding - apparently ObjectDataBuilder can produce an alignment of 0 int alignedOffset = section.Content.Count; if (objectData.Alignment > 1) { alignedOffset = (section.Content.Count + objectData.Alignment - 1) & -objectData.Alignment; int padding = alignedOffset - section.Content.Count; if (padding > 0) { if ((section.Characteristics & SectionCharacteristics.ContainsCode) != 0) { uint cp = _codePadding; while (padding >= sizeof(uint)) { section.Content.WriteUInt32(cp); padding -= sizeof(uint); } if (padding >= 2) { section.Content.WriteUInt16(unchecked ((ushort)cp)); cp >>= 16; } if ((padding & 1) != 0) { section.Content.WriteByte(unchecked ((byte)cp)); } } else { section.Content.WriteBytes(0, padding); } } } if (mapFile != null) { mapFile.WriteLine($@"S{sectionIndex}+0x{alignedOffset:X4}..{(alignedOffset + objectData.Data.Length):X4}: {objectData.Data.Length:X4} * {name}"); } section.Content.WriteBytes(objectData.Data); if (objectData.DefinedSymbols != null) { foreach (ISymbolDefinitionNode symbol in objectData.DefinedSymbols) { if (mapFile != null) { Utf8StringBuilder sb = new Utf8StringBuilder(); symbol.AppendMangledName(GetNameMangler(), sb); int sectionRelativeOffset = alignedOffset + symbol.Offset; mapFile.WriteLine($@" +0x{sectionRelativeOffset:X4}: {sb.ToString()}"); } _symbolMap.Add(symbol, new SymbolTarget( sectionIndex: sectionIndex, offset: alignedOffset + symbol.Offset, size: objectData.Data.Length)); } } if (objectData.Relocs != null && objectData.Relocs.Length != 0) { section.PlacedObjectDataToRelocate.Add(new PlacedObjectData(alignedOffset, objectData)); } }