summaryrefslogtreecommitdiff
path: root/src/cmd/ld/pe.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ld/pe.c')
-rw-r--r--src/cmd/ld/pe.c593
1 files changed, 593 insertions, 0 deletions
diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c
new file mode 100644
index 000000000..334c9959f
--- /dev/null
+++ b/src/cmd/ld/pe.c
@@ -0,0 +1,593 @@
+// 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
+};
+
+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 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;
+
+ if(nextsymoff == 0)
+ return;
+
+ size = nextsymoff + 4 + 18;
+ h = addpesection(".symtab", size, size);
+ h->Characteristics = IMAGE_SCN_MEM_READ|
+ IMAGE_SCN_MEM_DISCARDABLE;
+ chksectoff(h, cpos());
+ fh.PointerToSymbolTable = cpos();
+ fh.NumberOfSymbols = 1;
+ strnput("", 18); // one empty symbol
+ // put symbol string table
+ lputl(size);
+ 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;
+}
+
+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);
+
+ 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();
+
+ 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, 1);
+ 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);
+ set(SizeOfStackReserve, 0x0040000);
+ set(SizeOfStackCommit, 0x00001000);
+ set(SizeOfHeapReserve, 0x00100000);
+ set(SizeOfHeapCommit, 0x00001000);
+ set(NumberOfRvaAndSizes, 16);
+
+ pewrite();
+}