/// <summary> /// Update the PE file directories. Currently this is used to update the export symbol table /// when export symbols have been added to the section builder. /// </summary> /// <param name="directoriesBuilder">PE directory builder to update</param> public void UpdateDirectories(PEDirectoriesBuilder directoriesBuilder) { if (_corHeaderSymbol != null) { SymbolTarget symbolTarget = _symbolMap[_corHeaderSymbol]; Section section = _sections[symbolTarget.SectionIndex]; Debug.Assert(section.RVAWhenPlaced != 0); directoriesBuilder.CorHeaderTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _corHeaderSize); } if (_exportDirectoryEntry.Size != 0) { directoriesBuilder.ExportTable = _exportDirectoryEntry; } int relocationTableRVA = directoriesBuilder.BaseRelocationTable.RelativeVirtualAddress; if (relocationTableRVA == 0) { relocationTableRVA = _relocationDirectoryEntry.RelativeVirtualAddress; } directoriesBuilder.BaseRelocationTable = new DirectoryEntry( relocationTableRVA, directoriesBuilder.BaseRelocationTable.Size + _relocationDirectoryEntry.Size); if (_entryPointSymbol != null) { SymbolTarget symbolTarget = _symbolMap[_entryPointSymbol]; Section section = _sections[symbolTarget.SectionIndex]; Debug.Assert(section.RVAWhenPlaced != 0); directoriesBuilder.AddressOfEntryPoint = section.RVAWhenPlaced + symbolTarget.Offset; } }
/// <summary> /// Update the PE file directories. Currently this is used to update the export symbol table /// when export symbols have been added to the section builder. /// </summary> /// <param name="directoriesBuilder">PE directory builder to update</param> public void UpdateDirectories(PEDirectoriesBuilder directoriesBuilder) { if (_corHeaderSymbol != null) { SymbolTarget symbolTarget = _symbolMap[_corHeaderSymbol]; Section section = _sections[symbolTarget.SectionIndex]; Debug.Assert(section.RVAWhenPlaced != 0); directoriesBuilder.CorHeaderTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _corHeaderSize); } if (_win32ResourcesSymbol != null) { SymbolTarget symbolTarget = _symbolMap[_win32ResourcesSymbol]; Section section = _sections[symbolTarget.SectionIndex]; Debug.Assert(section.RVAWhenPlaced != 0); // Windows has a bug in its resource processing logic that occurs when // 1. A PE file is loaded as a data file // 2. The resource data found in the resources has an RVA which has a magnitude greater than the size of the section which holds the resources // 3. The offset of the start of the resource data from the start of the section is not zero. // // As it is impossible to effect condition 1 in the compiler, and changing condition 2 would require bloating the virtual size of the sections, // instead require that the resource data is located at offset 0 within the section. // We achieve that by sorting the Win32ResourcesNode as the first node. Debug.Assert(symbolTarget.Offset == 0); directoriesBuilder.ResourceTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _win32ResourcesSize); } if (_exportDirectoryEntry.Size != 0) { directoriesBuilder.ExportTable = _exportDirectoryEntry; } int relocationTableRVA = directoriesBuilder.BaseRelocationTable.RelativeVirtualAddress; if (relocationTableRVA == 0) { relocationTableRVA = _relocationDirectoryEntry.RelativeVirtualAddress; } directoriesBuilder.BaseRelocationTable = new DirectoryEntry( relocationTableRVA, directoriesBuilder.BaseRelocationTable.Size + _relocationDirectoryEntry.Size); if (_entryPointSymbol != null) { SymbolTarget symbolTarget = _symbolMap[_entryPointSymbol]; Section section = _sections[symbolTarget.SectionIndex]; Debug.Assert(section.RVAWhenPlaced != 0); directoriesBuilder.AddressOfEntryPoint = section.RVAWhenPlaced + symbolTarget.Offset; } if (_debugDirectorySymbol != null) { SymbolTarget symbolTarget = _symbolMap[_debugDirectorySymbol]; Section section = _sections[symbolTarget.SectionIndex]; Debug.Assert(section.RVAWhenPlaced != 0); directoriesBuilder.DebugTable = new DirectoryEntry(section.RVAWhenPlaced + symbolTarget.Offset, _debugDirectorySize); } }
/// <summary> /// Look up final file position for a given symbol. This assumes the section have already been placed. /// </summary> /// <param name="symbol">Symbol to look up</param> /// <returns>File position of the symbol, from the begining of the emitted image</returns> public int GetSymbolFilePosition(ISymbolNode symbol) { SymbolTarget symbolTarget = _symbolMap[symbol]; Section section = _sections[symbolTarget.SectionIndex]; Debug.Assert(section.RVAWhenPlaced != 0); return(section.FilePosWhenPlaced + symbolTarget.Offset); }
/// <summary> /// Update the COR header. /// </summary> /// <param name="corHeader">COR header builder to update</param> public void UpdateCorHeader(CorHeaderBuilder corHeader) { if (_readyToRunHeaderSymbol != null) { SymbolTarget headerTarget = _symbolMap[_readyToRunHeaderSymbol]; Section headerSection = _sections[headerTarget.SectionIndex]; Debug.Assert(headerSection.RVAWhenPlaced != 0); int r2rHeaderRVA = headerSection.RVAWhenPlaced + headerTarget.Offset; corHeader.ManagedNativeHeaderDirectory = new DirectoryEntry(r2rHeaderRVA, _readyToRunHeaderSize); } }
/// <summary> /// Relocate the produced PE file and output the result into a given stream. /// </summary> /// <param name="peFile">Blob builder representing the complete PE file</param> /// <param name="defaultImageBase">Default load address for the image</param> /// <param name="corHeaderBuilder">COR header</param> /// <param name="corHeaderFileOffset">File position of the COR header</param> /// <param name="outputStream">Stream to receive the relocated PE file</param> public void RelocateOutputFile( BlobBuilder peFile, ulong defaultImageBase, CorHeaderBuilder corHeaderBuilder, int corHeaderFileOffset, Stream outputStream) { RelocationHelper relocationHelper = new RelocationHelper(outputStream, defaultImageBase, peFile); if (corHeaderBuilder != null) { relocationHelper.CopyToFilePosition(corHeaderFileOffset); UpdateCorHeader(corHeaderBuilder); BlobBuilder corHeaderBlob = new BlobBuilder(); corHeaderBuilder.WriteTo(corHeaderBlob); int writtenSize = corHeaderBlob.Count; corHeaderBlob.WriteContentTo(outputStream); relocationHelper.AdvanceOutputPos(writtenSize); // Just skip the bytes that were emitted by the COR header writer byte[] skipBuffer = new byte[writtenSize]; relocationHelper.CopyBytesToBuffer(skipBuffer, writtenSize); } // Traverse relocations in all sections in their RVA order foreach (Section section in _sections.OrderBy((sec) => sec.RVAWhenPlaced)) { int rvaToFilePosDelta = section.FilePosWhenPlaced - section.RVAWhenPlaced; foreach (ObjectDataRelocations objectDataRelocs in section.Relocations) { foreach (Relocation relocation in objectDataRelocs.Relocs) { // Process a single relocation int relocationRVA = section.RVAWhenPlaced + objectDataRelocs.Offset + relocation.Offset; int relocationFilePos = relocationRVA + rvaToFilePosDelta; // Flush parts of PE file before the relocation to the output stream relocationHelper.CopyToFilePosition(relocationFilePos); // Look up relocation target SymbolTarget relocationTarget = _symbolMap[relocation.Target]; Section targetSection = _sections[relocationTarget.SectionIndex]; int targetRVA = targetSection.RVAWhenPlaced + relocationTarget.Offset; // Apply the relocation relocationHelper.ProcessRelocation(relocation.RelocType, relocationRVA, targetRVA); } } } // Flush remaining PE file blocks after the last relocation relocationHelper.CopyRestOfFile(); }
public void AddSymbolForRange(ISymbolNode symbol, ISymbolNode firstNode, ISymbolNode secondNode) { SymbolTarget firstSymbolTarget = _symbolMap[firstNode]; SymbolTarget secondSymbolTarget = _symbolMap[secondNode]; Debug.Assert(firstSymbolTarget.SectionIndex == secondSymbolTarget.SectionIndex); Debug.Assert(firstSymbolTarget.Offset <= secondSymbolTarget.Offset); _symbolMap.Add(symbol, new SymbolTarget( sectionIndex: firstSymbolTarget.SectionIndex, offset: firstSymbolTarget.Offset, size: secondSymbolTarget.Offset - firstSymbolTarget.Offset + secondSymbolTarget.Size )); }
/// <summary> /// Relocate the produced PE file and output the result into a given stream. /// </summary> /// <param name="peFile">Blob builder representing the complete PE file</param> /// <param name="defaultImageBase">Default load address for the image</param> /// <param name="corHeaderBuilder">COR header</param> /// <param name="corHeaderFileOffset">File position of the COR header</param> /// <param name="outputStream">Stream to receive the relocated PE file</param> public void RelocateOutputFile( BlobBuilder peFile, ulong defaultImageBase, Stream outputStream) { RelocationHelper relocationHelper = new RelocationHelper(outputStream, defaultImageBase, peFile); // Traverse relocations in all sections in their RVA order foreach (Section section in _sections.OrderBy((sec) => sec.RVAWhenPlaced)) { int rvaToFilePosDelta = section.FilePosWhenPlaced - section.RVAWhenPlaced; foreach (PlacedObjectData placedObjectData in section.PlacedObjectDataToRelocate) { foreach (Relocation relocation in placedObjectData.Relocs) { // Process a single relocation int relocationRVA = section.RVAWhenPlaced + placedObjectData.Offset + relocation.Offset; int relocationFilePos = relocationRVA + rvaToFilePosDelta; // Flush parts of PE file before the relocation to the output stream relocationHelper.CopyToFilePosition(relocationFilePos); // Look up relocation target SymbolTarget relocationTarget = _symbolMap[relocation.Target]; Section targetSection = _sections[relocationTarget.SectionIndex]; int targetRVA = targetSection.RVAWhenPlaced + relocationTarget.Offset; int filePosWhenPlaced = targetSection.FilePosWhenPlaced + relocationTarget.Offset; // If relocating to a node's size, switch out the target RVA with data length if (relocation.RelocType == RelocType.IMAGE_REL_SYMBOL_SIZE) { targetRVA = relocationTarget.Size; } // Apply the relocation relocationHelper.ProcessRelocation(relocation.RelocType, relocationRVA, targetRVA, filePosWhenPlaced); } } } // Flush remaining PE file blocks after the last relocation relocationHelper.CopyRestOfFile(); }
/// <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. 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 int addressTableRVA = sectionLocation.RelativeVirtualAddress + builder.Count; foreach (int addressTableEntry in addressTable) { builder.WriteInt32(addressTableEntry); } // Emit the export directory table 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); }