| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // PE (Portable Executable) file writing | |
| // http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx | |
| #include "l.h" | |
| #include "../ld/lib.h" | |
| #include "../ld/pe.h" | |
| #include "../ld/dwarf.h" | |
| // DOS stub that prints out | |
| // "This program cannot be run in DOS mode." | |
| static char dosstub[] = | |
| { | |
| 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00, | |
| 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, | |
| 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, | |
| 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, | |
| 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, | |
| 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, | |
| 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, | |
| 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, | |
| 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, | |
| 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, | |
| 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | |
| }; | |
| // Note: currently only up to 8 chars plus \0. | |
| static char *symlabels[] = { | |
| "symtab", "esymtab", "pclntab", "epclntab" | |
| }; | |
| static Sym *rsrcsym; | |
| static char symnames[256]; | |
| static int nextsymoff; | |
| int32 PESECTHEADR; | |
| int32 PEFILEHEADR; | |
| static int pe64; | |
| static int nsect; | |
| static int nextsectoff; | |
| static int nextfileoff; | |
| static int textsect; | |
| static IMAGE_FILE_HEADER fh; | |
| static IMAGE_OPTIONAL_HEADER oh; | |
| static PE64_IMAGE_OPTIONAL_HEADER oh64; | |
| static IMAGE_SECTION_HEADER sh[16]; | |
| static IMAGE_DATA_DIRECTORY* dd; | |
| #define set(n, v) (pe64 ? (oh64.n = v) : (oh.n = v)) | |
| #define put(v) (pe64 ? vputl(v) : lputl(v)) | |
| typedef struct Imp Imp; | |
| struct Imp { | |
| Sym* s; | |
| uvlong off; | |
| Imp* next; | |
| }; | |
| typedef struct Dll Dll; | |
| struct Dll { | |
| char* name; | |
| uvlong nameoff; | |
| uvlong thunkoff; | |
| Imp* ms; | |
| Dll* next; | |
| }; | |
| static Dll* dr; | |
| static Sym *dexport[1024]; | |
| static int nexport; | |
| static IMAGE_SECTION_HEADER* | |
| addpesection(char *name, int sectsize, int filesize) | |
| { | |
| IMAGE_SECTION_HEADER *h; | |
| if(nsect == 16) { | |
| diag("too many sections"); | |
| errorexit(); | |
| } | |
| h = &sh[nsect++]; | |
| strncpy((char*)h->Name, name, sizeof(h->Name)); | |
| h->VirtualSize = sectsize; | |
| h->VirtualAddress = nextsectoff; | |
| nextsectoff = rnd(nextsectoff+sectsize, PESECTALIGN); | |
| h->PointerToRawData = nextfileoff; | |
| if(filesize > 0) { | |
| h->SizeOfRawData = rnd(filesize, PEFILEALIGN); | |
| nextfileoff += h->SizeOfRawData; | |
| } | |
| return h; | |
| } | |
| static void | |
| chksectoff(IMAGE_SECTION_HEADER *h, vlong off) | |
| { | |
| if(off != h->PointerToRawData) { | |
| diag("%s.PointerToRawData = %#llux, want %#llux", (char *)h->Name, (vlong)h->PointerToRawData, off); | |
| errorexit(); | |
| } | |
| } | |
| static void | |
| chksectseg(IMAGE_SECTION_HEADER *h, Segment *s) | |
| { | |
| if(s->vaddr-PEBASE != h->VirtualAddress) { | |
| diag("%s.VirtualAddress = %#llux, want %#llux", (char *)h->Name, (vlong)h->VirtualAddress, (vlong)(s->vaddr-PEBASE)); | |
| errorexit(); | |
| } | |
| if(s->fileoff != h->PointerToRawData) { | |
| diag("%s.PointerToRawData = %#llux, want %#llux", (char *)h->Name, (vlong)h->PointerToRawData, (vlong)(s->fileoff)); | |
| errorexit(); | |
| } | |
| } | |
| void | |
| peinit(void) | |
| { | |
| int32 l; | |
| switch(thechar) { | |
| // 64-bit architectures | |
| case '6': | |
| pe64 = 1; | |
| l = sizeof(oh64); | |
| dd = oh64.DataDirectory; | |
| break; | |
| // 32-bit architectures | |
| default: | |
| l = sizeof(oh); | |
| dd = oh.DataDirectory; | |
| break; | |
| } | |
| PEFILEHEADR = rnd(sizeof(dosstub)+sizeof(fh)+l+sizeof(sh), PEFILEALIGN); | |
| PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); | |
| nextsectoff = PESECTHEADR; | |
| nextfileoff = PEFILEHEADR; | |
| } | |
| static void | |
| pewrite(void) | |
| { | |
| cseek(0); | |
| cwrite(dosstub, sizeof dosstub); | |
| strnput("PE", 4); | |
| // TODO: This code should not assume that the | |
| // memory representation is little-endian or | |
| // that the structs are packed identically to | |
| // their file representation. | |
| cwrite(&fh, sizeof fh); | |
| if(pe64) | |
| cwrite(&oh64, sizeof oh64); | |
| else | |
| cwrite(&oh, sizeof oh); | |
| cwrite(sh, nsect * sizeof sh[0]); | |
| } | |
| static void | |
| strput(char *s) | |
| { | |
| int n; | |
| for(n=0; *s; n++) | |
| cput(*s++); | |
| cput('\0'); | |
| n++; | |
| // string must be padded to even size | |
| if(n%2) | |
| cput('\0'); | |
| } | |
| static Dll* | |
| initdynimport(void) | |
| { | |
| Imp *m; | |
| Dll *d; | |
| Sym *s, *dynamic; | |
| dr = nil; | |
| m = nil; | |
| for(s = allsym; s != S; s = s->allsym) { | |
| if(!s->reachable || !s->dynimpname || s->dynexport) | |
| continue; | |
| for(d = dr; d != nil; d = d->next) { | |
| if(strcmp(d->name,s->dynimplib) == 0) { | |
| m = mal(sizeof *m); | |
| break; | |
| } | |
| } | |
| if(d == nil) { | |
| d = mal(sizeof *d); | |
| d->name = s->dynimplib; | |
| d->next = dr; | |
| dr = d; | |
| m = mal(sizeof *m); | |
| } | |
| m->s = s; | |
| m->next = d->ms; | |
| d->ms = m; | |
| } | |
| dynamic = lookup(".windynamic", 0); | |
| dynamic->reachable = 1; | |
| dynamic->type = SWINDOWS; | |
| for(d = dr; d != nil; d = d->next) { | |
| for(m = d->ms; m != nil; m = m->next) { | |
| m->s->type = SWINDOWS | SSUB; | |
| m->s->sub = dynamic->sub; | |
| dynamic->sub = m->s; | |
| m->s->value = dynamic->size; | |
| dynamic->size += PtrSize; | |
| } | |
| dynamic->size += PtrSize; | |
| } | |
| return dr; | |
| } | |
| static void | |
| addimports(IMAGE_SECTION_HEADER *datsect) | |
| { | |
| IMAGE_SECTION_HEADER *isect; | |
| uvlong n, oftbase, ftbase; | |
| vlong startoff, endoff; | |
| Imp *m; | |
| Dll *d; | |
| Sym* dynamic; | |
| startoff = cpos(); | |
| dynamic = lookup(".windynamic", 0); | |
| // skip import descriptor table (will write it later) | |
| n = 0; | |
| for(d = dr; d != nil; d = d->next) | |
| n++; | |
| cseek(startoff + sizeof(IMAGE_IMPORT_DESCRIPTOR) * (n + 1)); | |
| // write dll names | |
| for(d = dr; d != nil; d = d->next) { | |
| d->nameoff = cpos() - startoff; | |
| strput(d->name); | |
| } | |
| // write function names | |
| for(d = dr; d != nil; d = d->next) { | |
| for(m = d->ms; m != nil; m = m->next) { | |
| m->off = nextsectoff + cpos() - startoff; | |
| wputl(0); // hint | |
| strput(m->s->dynimpname); | |
| } | |
| } | |
| // write OriginalFirstThunks | |
| oftbase = cpos() - startoff; | |
| n = cpos(); | |
| for(d = dr; d != nil; d = d->next) { | |
| d->thunkoff = cpos() - n; | |
| for(m = d->ms; m != nil; m = m->next) | |
| put(m->off); | |
| put(0); | |
| } | |
| // add pe section and pad it at the end | |
| n = cpos() - startoff; | |
| isect = addpesection(".idata", n, n); | |
| isect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| | |
| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; | |
| chksectoff(isect, startoff); | |
| strnput("", isect->SizeOfRawData - n); | |
| endoff = cpos(); | |
| // write FirstThunks (allocated in .data section) | |
| ftbase = dynamic->value - datsect->VirtualAddress - PEBASE; | |
| cseek(datsect->PointerToRawData + ftbase); | |
| for(d = dr; d != nil; d = d->next) { | |
| for(m = d->ms; m != nil; m = m->next) | |
| put(m->off); | |
| put(0); | |
| } | |
| // finally write import descriptor table | |
| cseek(startoff); | |
| for(d = dr; d != nil; d = d->next) { | |
| lputl(isect->VirtualAddress + oftbase + d->thunkoff); | |
| lputl(0); | |
| lputl(0); | |
| lputl(isect->VirtualAddress + d->nameoff); | |
| lputl(datsect->VirtualAddress + ftbase + d->thunkoff); | |
| } | |
| lputl(0); //end | |
| lputl(0); | |
| lputl(0); | |
| lputl(0); | |
| lputl(0); | |
| // update data directory | |
| dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect->VirtualAddress; | |
| dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect->VirtualSize; | |
| dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = dynamic->value - PEBASE; | |
| dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = dynamic->size; | |
| cseek(endoff); | |
| } | |
| static int | |
| scmp(const void *p1, const void *p2) | |
| { | |
| Sym *s1, *s2; | |
| s1 = *(Sym**)p1; | |
| s2 = *(Sym**)p2; | |
| return strcmp(s1->dynimpname, s2->dynimpname); | |
| } | |
| static void | |
| initdynexport(void) | |
| { | |
| Sym *s; | |
| nexport = 0; | |
| for(s = allsym; s != S; s = s->allsym) { | |
| if(!s->reachable || !s->dynimpname || !s->dynexport) | |
| continue; | |
| if(nexport+1 > sizeof(dexport)/sizeof(dexport[0])) { | |
| diag("pe dynexport table is full"); | |
| errorexit(); | |
| } | |
| dexport[nexport] = s; | |
| nexport++; | |
| } | |
| qsort(dexport, nexport, sizeof dexport[0], scmp); | |
| } | |
| void | |
| addexports(void) | |
| { | |
| IMAGE_SECTION_HEADER *sect; | |
| IMAGE_EXPORT_DIRECTORY e; | |
| int size, i, va, va_name, va_addr, va_na, v; | |
| size = sizeof e + 10*nexport + strlen(outfile) + 1; | |
| for(i=0; i<nexport; i++) | |
| size += strlen(dexport[i]->dynimpname) + 1; | |
| if (nexport == 0) | |
| return; | |
| sect = addpesection(".edata", size, size); | |
| sect->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ; | |
| chksectoff(sect, cpos()); | |
| va = sect->VirtualAddress; | |
| dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = va; | |
| dd[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect->VirtualSize; | |
| va_name = va + sizeof e + nexport*4; | |
| va_addr = va + sizeof e; | |
| va_na = va + sizeof e + nexport*8; | |
| e.Characteristics = 0; | |
| e.MajorVersion = 0; | |
| e.MinorVersion = 0; | |
| e.NumberOfFunctions = nexport; | |
| e.NumberOfNames = nexport; | |
| e.Name = va + sizeof e + nexport*10; // Program names. | |
| e.Base = 1; | |
| e.AddressOfFunctions = va_addr; | |
| e.AddressOfNames = va_name; | |
| e.AddressOfNameOrdinals = va_na; | |
| // put IMAGE_EXPORT_DIRECTORY | |
| for (i=0; i<sizeof(e); i++) | |
| cput(((char*)&e)[i]); | |
| // put EXPORT Address Table | |
| for(i=0; i<nexport; i++) | |
| lputl(dexport[i]->value - PEBASE); | |
| // put EXPORT Name Pointer Table | |
| v = e.Name + strlen(outfile)+1; | |
| for(i=0; i<nexport; i++) { | |
| lputl(v); | |
| v += strlen(dexport[i]->dynimpname)+1; | |
| } | |
| // put EXPORT Ordinal Table | |
| for(i=0; i<nexport; i++) | |
| wputl(i); | |
| // put Names | |
| strnput(outfile, strlen(outfile)+1); | |
| for(i=0; i<nexport; i++) | |
| strnput(dexport[i]->dynimpname, strlen(dexport[i]->dynimpname)+1); | |
| strnput("", sect->SizeOfRawData - size); | |
| } | |
| void | |
| dope(void) | |
| { | |
| Sym *rel; | |
| /* relocation table */ | |
| rel = lookup(".rel", 0); | |
| rel->reachable = 1; | |
| rel->type = SELFROSECT; | |
| initdynimport(); | |
| initdynexport(); | |
| } | |
| /* | |
| * For more than 8 characters section names, name contains a slash (/) that is | |
| * followed by an ASCII representation of a decimal number that is an offset into | |
| * the string table. | |
| * reference: pecoff_v8.docx Page 24. | |
| * <http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx> | |
| */ | |
| IMAGE_SECTION_HEADER* | |
| newPEDWARFSection(char *name, vlong size) | |
| { | |
| IMAGE_SECTION_HEADER *h; | |
| char s[8]; | |
| if(size == 0) | |
| return nil; | |
| if(nextsymoff+strlen(name)+1 > sizeof(symnames)) { | |
| diag("pe string table is full"); | |
| errorexit(); | |
| } | |
| strcpy(&symnames[nextsymoff], name); | |
| sprint(s, "/%d\0", nextsymoff+4); | |
| nextsymoff += strlen(name); | |
| symnames[nextsymoff] = 0; | |
| nextsymoff ++; | |
| h = addpesection(s, size, size); | |
| h->Characteristics = IMAGE_SCN_MEM_READ| | |
| IMAGE_SCN_MEM_DISCARDABLE; | |
| return h; | |
| } | |
| static void | |
| addsymtable(void) | |
| { | |
| IMAGE_SECTION_HEADER *h; | |
| int i, size; | |
| Sym *s; | |
| fh.NumberOfSymbols = sizeof(symlabels)/sizeof(symlabels[0]); | |
| size = nextsymoff + 4 + 18*fh.NumberOfSymbols; | |
| h = addpesection(".symtab", size, size); | |
| h->Characteristics = IMAGE_SCN_MEM_READ| | |
| IMAGE_SCN_MEM_DISCARDABLE; | |
| chksectoff(h, cpos()); | |
| fh.PointerToSymbolTable = cpos(); | |
| // put COFF symbol table | |
| for (i=0; i<fh.NumberOfSymbols; i++) { | |
| s = rlookup(symlabels[i], 0); | |
| strnput(s->name, 8); | |
| lputl(datoff(s->value)); | |
| wputl(textsect); | |
| wputl(0x0308); // "array of structs" | |
| cput(2); // storage class: external | |
| cput(0); // no aux entries | |
| } | |
| // put COFF string table | |
| lputl(nextsymoff + 4); | |
| for (i=0; i<nextsymoff; i++) | |
| cput(symnames[i]); | |
| strnput("", h->SizeOfRawData - size); | |
| } | |
| void | |
| setpersrc(Sym *sym) | |
| { | |
| if(rsrcsym != nil) | |
| diag("too many .rsrc sections"); | |
| rsrcsym = sym; | |
| } | |
| void | |
| addpersrc(void) | |
| { | |
| IMAGE_SECTION_HEADER *h; | |
| uchar *p; | |
| uint32 val; | |
| Reloc *r; | |
| if(rsrcsym == nil) | |
| return; | |
| h = addpesection(".rsrc", rsrcsym->size, rsrcsym->size); | |
| h->Characteristics = IMAGE_SCN_MEM_READ| | |
| IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA; | |
| chksectoff(h, cpos()); | |
| // relocation | |
| for(r=rsrcsym->r; r<rsrcsym->r+rsrcsym->nr; r++) { | |
| p = rsrcsym->p + r->off; | |
| val = h->VirtualAddress + r->add; | |
| // 32-bit little-endian | |
| p[0] = val; | |
| p[1] = val>>8; | |
| p[2] = val>>16; | |
| p[3] = val>>24; | |
| } | |
| cwrite(rsrcsym->p, rsrcsym->size); | |
| strnput("", h->SizeOfRawData - rsrcsym->size); | |
| // update data directory | |
| dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h->VirtualAddress; | |
| dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h->VirtualSize; | |
| } | |
| static void | |
| addexcept(IMAGE_SECTION_HEADER *text) | |
| { | |
| IMAGE_SECTION_HEADER *pdata, *xdata; | |
| vlong startoff; | |
| uvlong n; | |
| Sym *sym; | |
| if(thechar != '6') | |
| return; | |
| // write unwind info | |
| sym = lookup("runtime.sigtramp", 0); | |
| startoff = cpos(); | |
| lputl(9); // version=1, flags=UNW_FLAG_EHANDLER, rest 0 | |
| lputl(sym->value - PEBASE); | |
| lputl(0); | |
| n = cpos() - startoff; | |
| xdata = addpesection(".xdata", n, n); | |
| xdata->Characteristics = IMAGE_SCN_MEM_READ| | |
| IMAGE_SCN_CNT_INITIALIZED_DATA; | |
| chksectoff(xdata, startoff); | |
| strnput("", xdata->SizeOfRawData - n); | |
| // write a function table entry for the whole text segment | |
| startoff = cpos(); | |
| lputl(text->VirtualAddress); | |
| lputl(text->VirtualAddress + text->VirtualSize); | |
| lputl(xdata->VirtualAddress); | |
| n = cpos() - startoff; | |
| pdata = addpesection(".pdata", n, n); | |
| pdata->Characteristics = IMAGE_SCN_MEM_READ| | |
| IMAGE_SCN_CNT_INITIALIZED_DATA; | |
| chksectoff(pdata, startoff); | |
| strnput("", pdata->SizeOfRawData - n); | |
| dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = pdata->VirtualAddress; | |
| dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = pdata->VirtualSize; | |
| } | |
| void | |
| asmbpe(void) | |
| { | |
| IMAGE_SECTION_HEADER *t, *d; | |
| switch(thechar) { | |
| default: | |
| diag("unknown PE architecture"); | |
| errorexit(); | |
| case '6': | |
| fh.Machine = IMAGE_FILE_MACHINE_AMD64; | |
| break; | |
| case '8': | |
| fh.Machine = IMAGE_FILE_MACHINE_I386; | |
| break; | |
| } | |
| t = addpesection(".text", segtext.len, segtext.len); | |
| t->Characteristics = IMAGE_SCN_CNT_CODE| | |
| IMAGE_SCN_CNT_INITIALIZED_DATA| | |
| IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; | |
| chksectseg(t, &segtext); | |
| textsect = nsect; | |
| d = addpesection(".data", segdata.len, segdata.filelen); | |
| d->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA| | |
| IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; | |
| chksectseg(d, &segdata); | |
| if(!debug['s']) | |
| dwarfaddpeheaders(); | |
| cseek(nextfileoff); | |
| addimports(d); | |
| addexports(); | |
| addsymtable(); | |
| addpersrc(); | |
| addexcept(t); | |
| fh.NumberOfSections = nsect; | |
| fh.TimeDateStamp = time(0); | |
| fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED| | |
| IMAGE_FILE_EXECUTABLE_IMAGE|IMAGE_FILE_DEBUG_STRIPPED; | |
| if (pe64) { | |
| fh.SizeOfOptionalHeader = sizeof(oh64); | |
| fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; | |
| set(Magic, 0x20b); // PE32+ | |
| } else { | |
| fh.SizeOfOptionalHeader = sizeof(oh); | |
| fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE; | |
| set(Magic, 0x10b); // PE32 | |
| oh.BaseOfData = d->VirtualAddress; | |
| } | |
| set(MajorLinkerVersion, 3); | |
| set(MinorLinkerVersion, 0); | |
| set(SizeOfCode, t->SizeOfRawData); | |
| set(SizeOfInitializedData, d->SizeOfRawData); | |
| set(SizeOfUninitializedData, 0); | |
| set(AddressOfEntryPoint, entryvalue()-PEBASE); | |
| set(BaseOfCode, t->VirtualAddress); | |
| set(ImageBase, PEBASE); | |
| set(SectionAlignment, PESECTALIGN); | |
| set(FileAlignment, PEFILEALIGN); | |
| set(MajorOperatingSystemVersion, 4); | |
| set(MinorOperatingSystemVersion, 0); | |
| set(MajorImageVersion, 1); | |
| set(MinorImageVersion, 0); | |
| set(MajorSubsystemVersion, 4); | |
| set(MinorSubsystemVersion, 0); | |
| set(SizeOfImage, nextsectoff); | |
| set(SizeOfHeaders, PEFILEHEADR); | |
| if(strcmp(headstring, "windowsgui") == 0) | |
| set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI); | |
| else | |
| set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_CUI); | |
| // Disable stack growth as we don't want Windows to | |
| // fiddle with the thread stack limits, which we set | |
| // ourselves to circumvent the stack checks in the | |
| // Windows exception dispatcher. | |
| // Commit size must be strictly less than reserve | |
| // size otherwise reserve will be rounded up to a | |
| // larger size, as verified with VMMap. | |
| set(SizeOfStackReserve, 0x00010000); | |
| set(SizeOfStackCommit, 0x0000ffff); | |
| set(SizeOfHeapReserve, 0x00100000); | |
| set(SizeOfHeapCommit, 0x00001000); | |
| set(NumberOfRvaAndSizes, 16); | |
| pewrite(); | |
| } |