1/* 2 Title: Export memory as a PE/COFF object 3 Author: David C. J. Matthews. 4 5 Copyright (c) 2006, 2011, 2016-18 David C. J. Matthews 6 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License version 2.1 as published by the Free Software Foundation. 11 12 This library is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR H PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with this library; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20*/ 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#elif defined(_WIN32) 24#include "winconfig.h" 25#else 26#error "No configuration file" 27#endif 28 29#include <stdio.h> 30#include <time.h> 31 32#ifdef HAVE_STDDEF_H 33#include <stddef.h> 34#endif 35 36#ifdef HAVE_STRING_H 37#include <string.h> 38#endif 39 40#ifdef HAVE_ERRNO_H 41#include <errno.h> 42#endif 43 44#ifdef HAVE_ASSERT_H 45#include <assert.h> 46#endif 47 48#include <windows.h> 49 50#include "globals.h" 51#include "pecoffexport.h" 52#include "machine_dep.h" 53#include "scanaddrs.h" 54#include "run_time.h" 55#include "../polyexports.h" 56#include "version.h" 57#include "polystring.h" 58#include "timing.h" 59 60#ifdef _DEBUG 61/* MS C defines _DEBUG for debug builds. */ 62#define DEBUG 63#endif 64 65#ifdef DEBUG 66#define ASSERT(x) assert(x) 67#else 68#define ASSERT(x) 69#endif 70 71#if (SIZEOF_VOIDP == 8) 72#define DIRECT_WORD_RELOCATION IMAGE_REL_AMD64_ADDR64 73#define RELATIVE_32BIT_RELOCATION IMAGE_REL_AMD64_REL32 74#else 75#define DIRECT_WORD_RELOCATION IMAGE_REL_I386_DIR32 76#define RELATIVE_32BIT_RELOCATION IMAGE_REL_I386_REL32 77#endif 78 79void PECOFFExport::writeRelocation(const IMAGE_RELOCATION* reloc) 80{ 81 fwrite(reloc, sizeof(*reloc), 1, exportFile); 82 if (relocationCount == 0) 83 firstRelocation = *reloc; 84 relocationCount++; 85} 86 87void PECOFFExport::addExternalReference(void *relocAddr, const char *name, bool/* isFuncPtr*/) 88{ 89 externTable.makeEntry(name); 90 IMAGE_RELOCATION reloc; 91 // Set the offset within the section we're scanning. 92 setRelocationAddress(relocAddr, &reloc.VirtualAddress); 93 reloc.SymbolTableIndex = symbolNum++; 94 reloc.Type = DIRECT_WORD_RELOCATION; 95 writeRelocation(&reloc); 96} 97 98// Generate the address relative to the start of the segment. 99void PECOFFExport::setRelocationAddress(void *p, DWORD *reloc) 100{ 101 unsigned area = findArea(p); 102 DWORD offset = (DWORD)((char*)p - (char*)memTable[area].mtOriginalAddr); 103 *reloc = offset; 104} 105 106// Create a relocation entry for an address at a given location. 107PolyWord PECOFFExport::createRelocation(PolyWord p, void *relocAddr) 108{ 109 IMAGE_RELOCATION reloc; 110 // Set the offset within the section we're scanning. 111 setRelocationAddress(relocAddr, &reloc.VirtualAddress); 112 void *addr = p.AsAddress(); 113 unsigned addrArea = findArea(addr); 114 POLYUNSIGNED offset = (POLYUNSIGNED)((char*)addr - (char*)memTable[addrArea].mtOriginalAddr); 115 reloc.SymbolTableIndex = addrArea; 116 reloc.Type = DIRECT_WORD_RELOCATION; 117 writeRelocation(&reloc); 118 return PolyWord::FromUnsigned(offset); 119} 120 121#ifdef SYMBOLS_REQUIRE_UNDERSCORE 122#define POLY_PREFIX_STRING "_" 123#else 124#define POLY_PREFIX_STRING "" 125#endif 126 127void PECOFFExport::writeSymbol(const char *symbolName, __int32 value, int section, bool isExtern, int symType) 128{ 129 // On X86/32 we have to add an underscore to external symbols 130 TempCString fullSymbol; 131 fullSymbol = (char*)malloc(strlen(POLY_PREFIX_STRING) + strlen(symbolName) + 1); 132 if (fullSymbol == 0) throw MemoryException(); 133 sprintf(fullSymbol, "%s%s", POLY_PREFIX_STRING, symbolName); 134 IMAGE_SYMBOL symbol; 135 memset(&symbol, 0, sizeof(symbol)); // Zero the unused part of the string 136 // Short symbol names go in the entry, longer ones go in the string table. 137 if (strlen(fullSymbol) <= 8) 138 strcat((char*)symbol.N.ShortName, fullSymbol); 139 else { 140 symbol.N.Name.Short = 0; 141 // We have to add 4 bytes because the first word written to the file is a length word. 142 symbol.N.Name.Long = stringTable.makeEntry(fullSymbol) + sizeof(unsigned); 143 } 144 symbol.Value = value; 145 symbol.SectionNumber = section; 146 symbol.Type = symType; 147 symbol.StorageClass = isExtern ? IMAGE_SYM_CLASS_EXTERNAL : IMAGE_SYM_CLASS_STATIC; 148 fwrite(&symbol, sizeof(symbol), 1, exportFile); 149} 150 151/* This is called for each constant within the code. 152 Print a relocation entry for the word and return a value that means 153 that the offset is saved in original word. */ 154void PECOFFExport::ScanConstant(PolyObject *base, byte *addr, ScanRelocationKind code) 155{ 156#ifndef POLYML32IN64 157 IMAGE_RELOCATION reloc; 158 PolyObject *p = GetConstantValue(addr, code); 159 160 if (p == 0) 161 return; 162 163 void *a = p; 164 unsigned aArea = findArea(a); 165 166 // We don't need a relocation if this is relative to the current segment 167 // since the relative address will already be right. 168 if (code == PROCESS_RELOC_I386RELATIVE && aArea == findArea(addr)) 169 return; 170 171 setRelocationAddress(addr, &reloc.VirtualAddress); 172 // Set the value at the address to the offset relative to the symbol. 173 uintptr_t offset = (char*)a - (char*)memTable[aArea].mtOriginalAddr; 174 reloc.SymbolTableIndex = aArea; 175 176 // The value we store here is the offset whichever relocation method 177 // we're using. 178 unsigned maxSize = code == PROCESS_RELOC_I386RELATIVE ? 4: sizeof(PolyWord); 179 for (unsigned i = 0; i < maxSize; i++) 180 { 181 addr[i] = (byte)(offset & 0xff); 182 offset >>= 8; 183 } 184 185 if (code == PROCESS_RELOC_I386RELATIVE) 186 reloc.Type = RELATIVE_32BIT_RELOCATION; 187 else 188 reloc.Type = DIRECT_WORD_RELOCATION; 189 190 writeRelocation(&reloc); 191#endif 192} 193 194// Set the file alignment. 195void PECOFFExport::alignFile(int align) 196{ 197 char pad[32] = {0}; // Maximum alignment 198 int offset = ftell(exportFile); 199 if ((offset % align) == 0) return; 200 fwrite(&pad, align - (offset % align), 1, exportFile); 201} 202 203void PECOFFExport::exportStore(void) 204{ 205 PolyWord *p; 206 IMAGE_FILE_HEADER fhdr; 207 IMAGE_SECTION_HEADER *sections = 0; 208 IMAGE_RELOCATION reloc; 209 unsigned i; 210 // These are written out as the description of the data. 211 exportDescription exports; 212 time_t now = getBuildTime(); 213 214 sections = new IMAGE_SECTION_HEADER [memTableEntries+1]; // Plus one for the tables. 215 216 // Write out initial values for the headers. These are overwritten at the end. 217 // File header 218 memset(&fhdr, 0, sizeof(fhdr)); 219#if (SIZEOF_VOIDP == 8) 220 fhdr.Machine = IMAGE_FILE_MACHINE_AMD64; // x86-64 221#else 222 fhdr.Machine = IMAGE_FILE_MACHINE_I386; // i386 223#endif 224 fhdr.NumberOfSections = memTableEntries+1; // One for each area plus one for the tables. 225 fhdr.TimeDateStamp = (DWORD)now; 226 //fhdr.NumberOfSymbols = memTableEntries+1; // One for each area plus "poly_exports" 227 fwrite(&fhdr, sizeof(fhdr), 1, exportFile); // Write it for the moment. 228 229 // External symbols are added after the memory table entries and "poly_exports". 230 symbolNum = memTableEntries+1; // The first external symbol 231 232 // Section headers. 233 for (i = 0; i < memTableEntries; i++) 234 { 235 memset(§ions[i], 0, sizeof(IMAGE_SECTION_HEADER)); 236 sections[i].SizeOfRawData = (DWORD)memTable[i].mtLength; 237 sections[i].Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_ALIGN_8BYTES; 238 239 if (memTable[i].mtFlags & MTF_WRITEABLE) 240 { 241 // Mutable data 242 ASSERT(!(memTable[i].mtFlags & MTF_EXECUTABLE)); // Executable areas can't be writable. 243 strcpy((char*)sections[i].Name, ".data"); 244 sections[i].Characteristics |= IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA; 245 } 246#ifndef CODEISNOTEXECUTABLE 247 // Not if we're building the interpreted version. 248 else if (memTable[i].mtFlags & MTF_EXECUTABLE) 249 { 250 // Immutable data areas are marked as executable. 251 strcpy((char*)sections[i].Name, ".text"); 252 sections[i].Characteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE; 253 } 254#endif 255 else 256 { 257 // Immutable data areas are marked as executable. 258 strcpy((char*)sections[i].Name, ".rdata"); 259 sections[i].Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; 260 } 261 } 262 // Extra section for the tables. 263 memset(§ions[memTableEntries], 0, sizeof(IMAGE_SECTION_HEADER)); 264 sprintf((char*)sections[memTableEntries].Name, ".data"); 265 sections[memTableEntries].SizeOfRawData = sizeof(exports) + (memTableEntries+1)*sizeof(memoryTableEntry); 266 // Don't need write access here but keep it for consistency with other .data sections 267 sections[memTableEntries].Characteristics = 268 IMAGE_SCN_MEM_READ | IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA; 269 270 fwrite(sections, sizeof(IMAGE_SECTION_HEADER), memTableEntries+1, exportFile); // Write it for the moment. 271 272 for (i = 0; i < memTableEntries; i++) 273 { 274 sections[i].PointerToRelocations = ftell(exportFile); 275 relocationCount = 0; 276 277 // Create the relocation table and turn all addresses into offsets. 278 char *start = (char*)memTable[i].mtOriginalAddr; 279 char *end = start + memTable[i].mtLength; 280 for (p = (PolyWord*)start; p < (PolyWord*)end; ) 281 { 282 p++; 283 PolyObject *obj = (PolyObject*)p; 284 POLYUNSIGNED length = obj->Length(); 285 // Update any constants before processing the object 286 // We need that for relative jumps/calls in X86/64. 287 if (length != 0 && obj->IsCodeObject()) 288 machineDependent->ScanConstantsWithinCode(obj, this); 289 relocateObject(obj); 290 p += length; 291 } 292 // If there are more than 64k relocations set this bit and set the value to 64k-1. 293 if (relocationCount >= 65535) { 294 // We're going to overwrite the first relocation so we have to write the 295 // copy we saved here. 296 writeRelocation(&firstRelocation); // Increments relocationCount 297 sections[i].NumberOfRelocations = 65535; 298 sections[i].Characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL; 299 // We have to go back and patch up the first (dummy) relocation entry 300 // which contains the count. 301 fseek(exportFile, sections[i].PointerToRelocations, SEEK_SET); 302 memset(&reloc, 0, sizeof(reloc)); 303 reloc.RelocCount = relocationCount; 304 fwrite(&reloc, sizeof(reloc), 1, exportFile); 305 fseek(exportFile, 0, SEEK_END); // Return to the end of the file. 306 } 307 else sections[i].NumberOfRelocations = relocationCount; 308 } 309 310 // We don't need to handle relocation overflow here. 311 sections[memTableEntries].PointerToRelocations = ftell(exportFile); 312 relocationCount = 0; 313 314 // Relocations for "exports" and "memTable"; 315 316 // Address of "memTable" within "exports". We can't use createRelocation because 317 // the position of the relocation is not in either the mutable or the immutable area. 318 reloc.Type = DIRECT_WORD_RELOCATION; 319 reloc.SymbolTableIndex = memTableEntries; // Relative to poly_exports 320 reloc.VirtualAddress = offsetof(exportDescription, memTable); 321 fwrite(&reloc, sizeof(reloc), 1, exportFile); 322 relocationCount++; 323 324 // Address of "rootFunction" within "exports" 325 reloc.Type = DIRECT_WORD_RELOCATION; 326 unsigned rootAddrArea = findArea(rootFunction); 327 reloc.SymbolTableIndex = rootAddrArea; 328 reloc.VirtualAddress = offsetof(exportDescription, rootFunction); 329 fwrite(&reloc, sizeof(reloc), 1, exportFile); 330 relocationCount++; 331 332 for (i = 0; i < memTableEntries; i++) 333 { 334 reloc.Type = DIRECT_WORD_RELOCATION; 335 reloc.SymbolTableIndex = i; // Relative to base symbol 336 reloc.VirtualAddress = 337 sizeof(exportDescription) + i * sizeof(memoryTableEntry) + offsetof(memoryTableEntry, mtCurrentAddr); 338 fwrite(&reloc, sizeof(reloc), 1, exportFile); 339 relocationCount++; 340 } 341 342 ASSERT(relocationCount < 65535); // Shouldn't get overflow!! 343 sections[memTableEntries].NumberOfRelocations = relocationCount; 344 345 // Now the binary data. 346 for (i = 0; i < memTableEntries; i++) 347 { 348 sections[i].PointerToRawData = ftell(exportFile); 349 fwrite(memTable[i].mtOriginalAddr, 1, memTable[i].mtLength, exportFile); 350 } 351 352 sections[memTableEntries].PointerToRawData = ftell(exportFile); 353 memset(&exports, 0, sizeof(exports)); 354 exports.structLength = sizeof(exportDescription); 355 exports.memTableSize = sizeof(memoryTableEntry); 356 exports.memTableEntries = memTableEntries; 357 exports.memTable = (memoryTableEntry *)sizeof(exports); // It follows immediately after this. 358 exports.rootFunction = (void*)((char*)rootFunction - (char*)memTable[rootAddrArea].mtOriginalAddr); 359 exports.timeStamp = now; 360 exports.architecture = machineDependent->MachineArchitecture(); 361 exports.rtsVersion = POLY_version_number; 362#ifdef POLYML32IN64 363 exports.originalBaseAddr = globalHeapBase; 364#else 365 exports.originalBaseAddr = 0; 366#endif 367 368 // Set the address values to zero before we write. They will always 369 // be relative to their base symbol. 370 for (i = 0; i < memTableEntries; i++) 371 memTable[i].mtCurrentAddr = 0; 372 373 fwrite(&exports, sizeof(exports), 1, exportFile); 374 fwrite(memTable, sizeof(memoryTableEntry), memTableEntries, exportFile); 375 // First the symbol table. We have one entry for the exports and an additional 376 // entry for each of the sections. 377 fhdr.PointerToSymbolTable = ftell(exportFile); 378 379 // The section numbers are one-based. Zero indicates the "common" area. 380 // First write symbols for each section and for poly_exports. 381 for (i = 0; i < memTableEntries; i++) 382 { 383 char buff[50]; 384 sprintf(buff, "area%0d", i); 385 writeSymbol(buff, 0, i+1, false); 386 } 387 388 // Exported symbol for table. 389 writeSymbol("poly_exports", 0, memTableEntries+1, true); 390 391 // External references. 392 for (unsigned i = 0; i < externTable.stringSize; i += (unsigned)strlen(externTable.strings+i) + 1) 393 writeSymbol(externTable.strings+i, 0, 0, true, 0x20); 394 395 fhdr.NumberOfSymbols = symbolNum; 396 397 // The string table is written immediately after the symbols. 398 // The length is included as the first word. 399 unsigned strSize = stringTable.stringSize + sizeof(unsigned); 400 fwrite(&strSize, sizeof(strSize), 1, exportFile); 401 fwrite(stringTable.strings, stringTable.stringSize, 1, exportFile); 402 403 // Rewind to rewrite the headers. 404 fseek(exportFile, 0, SEEK_SET); 405 fwrite(&fhdr, sizeof(fhdr), 1, exportFile); 406 fwrite(sections, sizeof(IMAGE_SECTION_HEADER), memTableEntries+1, exportFile); 407 408 fclose(exportFile); exportFile = NULL; 409 delete[](sections); 410} 411 412