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(&sections[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(&sections[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