tinycc/tccpe.c
grischka 5280293d6b make: create native tcc from separate objects
This was already possible using
    make NOTALLINONE=1
and is now the default.

To build as previously from one big source, use
    make ONE_SOURCE=1

Cross compilers are still build from one source because using
separate objects requires separate build directories one per
platform which currently is not (yet) supported by the makefile.

We could probably use gnu-makeish target variables like
    $(I386_CROSS): OUTDIR=build/i386
    $(X64_CROSS): OUTDIR=build/x86-64
and so on ...

Also NEED_FLOAT_TYPES for arm-gen is removed.  It was about
variables that are referenced from outside (libtcc, tccgen).
We could declare them in tcc.h (as with reg_classes) or have
them twice in arm-gen.c.  I chose option 2.
2011-07-14 18:45:37 +02:00

1909 lines
57 KiB
C

/*
* TCCPE.C - PE file output for the Tiny C Compiler
*
* Copyright (c) 2005-2007 grischka
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tcc.h"
#ifndef _WIN32
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
#define PE_MERGE_DATA
// #define PE_PRINT_SECTIONS
#ifdef TCC_TARGET_X86_64
# define ADDR3264 ULONGLONG
#else
# define ADDR3264 DWORD
#endif
#if defined _WIN32 && (defined _WIN64) == (defined TCC_TARGET_X86_64)
#define TCC_IS_NATIVE
#endif
#ifdef _WIN32
void dbg_printf (const char *fmt, ...)
{
char buffer[4000];
va_list arg;
int x;
va_start(arg, fmt);
x = vsprintf (buffer, fmt, arg);
strcpy(buffer+x, "\n");
OutputDebugString(buffer);
}
#endif
/* ----------------------------------------------------------- */
#ifndef IMAGE_NT_SIGNATURE
/* ----------------------------------------------------------- */
/* definitions below are from winnt.h */
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned long long ULONGLONG;
#pragma pack(push, 1)
typedef struct _IMAGE_DOS_HEADER { /* DOS .EXE header */
WORD e_magic; /* Magic number */
WORD e_cblp; /* Bytes on last page of file */
WORD e_cp; /* Pages in file */
WORD e_crlc; /* Relocations */
WORD e_cparhdr; /* Size of header in paragraphs */
WORD e_minalloc; /* Minimum extra paragraphs needed */
WORD e_maxalloc; /* Maximum extra paragraphs needed */
WORD e_ss; /* Initial (relative) SS value */
WORD e_sp; /* Initial SP value */
WORD e_csum; /* Checksum */
WORD e_ip; /* Initial IP value */
WORD e_cs; /* Initial (relative) CS value */
WORD e_lfarlc; /* File address of relocation table */
WORD e_ovno; /* Overlay number */
WORD e_res[4]; /* Reserved words */
WORD e_oemid; /* OEM identifier (for e_oeminfo) */
WORD e_oeminfo; /* OEM information; e_oemid specific */
WORD e_res2[10]; /* Reserved words */
DWORD e_lfanew; /* File address of new exe header */
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
#define SIZE_OF_NT_SIGNATURE 4
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
#define IMAGE_SIZEOF_FILE_HEADER 20
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
typedef struct _IMAGE_OPTIONAL_HEADER {
/* Standard fields. */
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
#ifndef TCC_TARGET_X86_64
DWORD BaseOfData;
#endif
/* NT additional fields. */
ADDR3264 ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ADDR3264 SizeOfStackReserve;
ADDR3264 SizeOfStackCommit;
ADDR3264 SizeOfHeapReserve;
ADDR3264 SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, IMAGE_OPTIONAL_HEADER;
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 /* Exception Directory */
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 /* Security Directory */
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 /* Base Relocation Table */
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 /* Debug Directory */
/* IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 (X86 usage) */
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 /* Architecture Specific Data */
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* RVA of GP */
#define IMAGE_DIRECTORY_ENTRY_TLS 9 /* TLS Directory */
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 /* Load Configuration Directory */
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 /* Bound Import Directory in headers */
#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 /* Delay Load Import Descriptors */
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 /* COM Runtime descriptor */
/* Section header format. */
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
#define IMAGE_SIZEOF_SECTION_HEADER 40
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions;
DWORD AddressOfNames;
DWORD AddressOfNameOrdinals;
} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
#define IMAGE_SIZEOF_BASE_RELOCATION 8
#define IMAGE_REL_BASED_ABSOLUTE 0
#define IMAGE_REL_BASED_HIGH 1
#define IMAGE_REL_BASED_LOW 2
#define IMAGE_REL_BASED_HIGHLOW 3
#define IMAGE_REL_BASED_HIGHADJ 4
#define IMAGE_REL_BASED_MIPS_JMPADDR 5
#define IMAGE_REL_BASED_SECTION 6
#define IMAGE_REL_BASED_REL32 7
#pragma pack(pop)
/* ----------------------------------------------------------- */
#endif /* ndef IMAGE_NT_SIGNATURE */
/* ----------------------------------------------------------- */
#pragma pack(push, 1)
struct pe_header
{
IMAGE_DOS_HEADER doshdr;
BYTE dosstub[0x40];
DWORD nt_sig;
IMAGE_FILE_HEADER filehdr;
#ifdef TCC_TARGET_X86_64
IMAGE_OPTIONAL_HEADER64 opthdr;
#else
#ifdef _WIN64
IMAGE_OPTIONAL_HEADER32 opthdr;
#else
IMAGE_OPTIONAL_HEADER opthdr;
#endif
#endif
};
struct pe_reloc_header {
DWORD offset;
DWORD size;
};
struct pe_rsrc_header {
struct _IMAGE_FILE_HEADER filehdr;
struct _IMAGE_SECTION_HEADER sectionhdr;
};
struct pe_rsrc_reloc {
DWORD offset;
DWORD size;
WORD type;
};
#pragma pack(pop)
/* ------------------------------------------------------------- */
/* internal temporary structures */
/*
#define IMAGE_SCN_CNT_CODE 0x00000020
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
#define IMAGE_SCN_MEM_SHARED 0x10000000
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
#define IMAGE_SCN_MEM_READ 0x40000000
#define IMAGE_SCN_MEM_WRITE 0x80000000
*/
enum {
sec_text = 0,
sec_data ,
sec_bss ,
sec_idata ,
sec_pdata ,
sec_other ,
sec_rsrc ,
sec_stab ,
sec_reloc ,
sec_last
};
static const DWORD pe_sec_flags[] = {
0x60000020, /* ".text" , */
0xC0000040, /* ".data" , */
0xC0000080, /* ".bss" , */
0x40000040, /* ".idata" , */
0x40000040, /* ".pdata" , */
0xE0000060, /* < other > , */
0x40000040, /* ".rsrc" , */
0x42000802, /* ".stab" , */
0x42000040, /* ".reloc" , */
};
struct section_info {
int cls, ord;
char name[32];
DWORD sh_addr;
DWORD sh_size;
DWORD sh_flags;
unsigned char *data;
DWORD data_size;
IMAGE_SECTION_HEADER ish;
};
struct import_symbol {
int sym_index;
int iat_index;
int thk_offset;
};
struct pe_import_info {
int dll_index;
int sym_count;
struct import_symbol **symbols;
};
struct pe_info {
TCCState *s1;
Section *reloc;
Section *thunk;
const char *filename;
int type;
DWORD sizeofheaders;
DWORD imagebase;
DWORD start_addr;
DWORD imp_offs;
DWORD imp_size;
DWORD iat_offs;
DWORD iat_size;
DWORD exp_offs;
DWORD exp_size;
int subsystem;
DWORD section_align;
DWORD file_align;
struct section_info *sec_info;
int sec_count;
struct pe_import_info **imp_info;
int imp_count;
};
#define PE_NUL 0
#define PE_DLL 1
#define PE_GUI 2
#define PE_EXE 3
#define PE_RUN 4
/* --------------------------------------------*/
static const char *pe_export_name(TCCState *s1, ElfW(Sym) *sym)
{
const char *name = symtab_section->link->data + sym->st_name;
if (s1->leading_underscore && name[0] == '_' && !(sym->st_other & 2))
return name + 1;
return name;
}
static int pe_find_import(TCCState * s1, ElfW(Sym) *sym)
{
char buffer[200];
const char *s, *p;
int sym_index = 0, n = 0;
do {
s = pe_export_name(s1, sym);
if (n) {
/* second try: */
if (sym->st_other & 2) {
/* try w/0 stdcall deco (windows API convention) */
p = strrchr(s, '@');
if (!p || s[0] != '_')
break;
strcpy(buffer, s+1)[p-s-1] = 0;
} else if (s[0] != '_') { /* try non-ansi function */
buffer[0] = '_', strcpy(buffer + 1, s);
} else if (0 == memcmp(s, "__imp__", 7)) { /* mingw 2.0 */
strcpy(buffer, s + 6);
} else if (0 == memcmp(s, "_imp___", 7)) { /* mingw 3.7 */
strcpy(buffer, s + 6);
} else {
break;
}
s = buffer;
}
sym_index = find_elf_sym(s1->dynsymtab_section, s);
// printf("find (%d) %d %s\n", n, sym_index, s);
} while (0 == sym_index && ++n < 2);
return sym_index;
}
/*----------------------------------------------------------------------------*/
static int dynarray_assoc(void **pp, int n, int key)
{
int i;
for (i = 0; i < n; ++i, ++pp)
if (key == **(int **) pp)
return i;
return -1;
}
#if 0
ST_FN DWORD umin(DWORD a, DWORD b)
{
return a < b ? a : b;
}
#endif
static DWORD umax(DWORD a, DWORD b)
{
return a < b ? b : a;
}
static DWORD pe_file_align(struct pe_info *pe, DWORD n)
{
return (n + (pe->file_align - 1)) & ~(pe->file_align - 1);
}
static DWORD pe_virtual_align(struct pe_info *pe, DWORD n)
{
return (n + (pe->section_align - 1)) & ~(pe->section_align - 1);
}
static void pe_align_section(Section *s, int a)
{
int i = s->data_offset & (a-1);
if (i)
section_ptr_add(s, a - i);
}
static void pe_set_datadir(struct pe_header *hdr, int dir, DWORD addr, DWORD size)
{
hdr->opthdr.DataDirectory[dir].VirtualAddress = addr;
hdr->opthdr.DataDirectory[dir].Size = size;
}
static int pe_fwrite(void *data, unsigned len, FILE *fp, DWORD *psum)
{
if (psum) {
DWORD sum = *psum;
WORD *p = data;
int i;
for (i = len; i > 0; i -= 2) {
sum += (i >= 2) ? *p++ : *(BYTE*)p;
sum = (sum + (sum >> 16)) & 0xFFFF;
}
*psum = sum;
}
return len == fwrite(data, 1, len, fp) ? 0 : -1;
}
static void pe_fpad(FILE *fp, DWORD new_pos)
{
DWORD pos = ftell(fp);
while (++pos <= new_pos)
fputc(0, fp);
}
/*----------------------------------------------------------------------------*/
static int pe_write(struct pe_info *pe)
{
static const struct pe_header pe_template = {
{
/* IMAGE_DOS_HEADER doshdr */
0x5A4D, /*WORD e_magic; Magic number */
0x0090, /*WORD e_cblp; Bytes on last page of file */
0x0003, /*WORD e_cp; Pages in file */
0x0000, /*WORD e_crlc; Relocations */
0x0004, /*WORD e_cparhdr; Size of header in paragraphs */
0x0000, /*WORD e_minalloc; Minimum extra paragraphs needed */
0xFFFF, /*WORD e_maxalloc; Maximum extra paragraphs needed */
0x0000, /*WORD e_ss; Initial (relative) SS value */
0x00B8, /*WORD e_sp; Initial SP value */
0x0000, /*WORD e_csum; Checksum */
0x0000, /*WORD e_ip; Initial IP value */
0x0000, /*WORD e_cs; Initial (relative) CS value */
0x0040, /*WORD e_lfarlc; File address of relocation table */
0x0000, /*WORD e_ovno; Overlay number */
{0,0,0,0}, /*WORD e_res[4]; Reserved words */
0x0000, /*WORD e_oemid; OEM identifier (for e_oeminfo) */
0x0000, /*WORD e_oeminfo; OEM information; e_oemid specific */
{0,0,0,0,0,0,0,0,0,0}, /*WORD e_res2[10]; Reserved words */
0x00000080 /*DWORD e_lfanew; File address of new exe header */
},{
/* BYTE dosstub[0x40] */
/* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 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,
},
0x00004550, /* DWORD nt_sig = IMAGE_NT_SIGNATURE */
{
/* IMAGE_FILE_HEADER filehdr */
#if defined(TCC_TARGET_I386)
0x014C, /*WORD Machine; */
#elif defined(TCC_TARGET_X86_64)
0x8664, /*WORD Machine; */
#elif defined(TCC_TARGET_ARM)
0x01C0, /*WORD Machine; */
#endif
0x0003, /*WORD NumberOfSections; */
0x00000000, /*DWORD TimeDateStamp; */
0x00000000, /*DWORD PointerToSymbolTable; */
0x00000000, /*DWORD NumberOfSymbols; */
#if defined(TCC_TARGET_X86_64)
0x00F0, /*WORD SizeOfOptionalHeader; */
0x022F /*WORD Characteristics; */
#define CHARACTERISTICS_DLL 0x222E
#elif defined(TCC_TARGET_I386)
0x00E0, /*WORD SizeOfOptionalHeader; */
0x030F /*WORD Characteristics; */
#define CHARACTERISTICS_DLL 0x230E
#elif defined(TCC_TARGET_ARM)
0x00E0, /*WORD SizeOfOptionalHeader; */
0x010F, /*WORD Characteristics; */
#define CHARACTERISTICS_DLL 0x230F
#endif
},{
/* IMAGE_OPTIONAL_HEADER opthdr */
/* Standard fields. */
#ifdef TCC_TARGET_X86_64
0x020B, /*WORD Magic; */
#else
0x010B, /*WORD Magic; */
#endif
0x06, /*BYTE MajorLinkerVersion; */
0x00, /*BYTE MinorLinkerVersion; */
0x00000000, /*DWORD SizeOfCode; */
0x00000000, /*DWORD SizeOfInitializedData; */
0x00000000, /*DWORD SizeOfUninitializedData; */
0x00000000, /*DWORD AddressOfEntryPoint; */
0x00000000, /*DWORD BaseOfCode; */
#ifndef TCC_TARGET_X86_64
0x00000000, /*DWORD BaseOfData; */
#endif
/* NT additional fields. */
#if defined(TCC_TARGET_ARM)
0x00100000, /*DWORD ImageBase; */
#else
0x00400000, /*DWORD ImageBase; */
#endif
0x00001000, /*DWORD SectionAlignment; */
0x00000200, /*DWORD FileAlignment; */
0x0004, /*WORD MajorOperatingSystemVersion; */
0x0000, /*WORD MinorOperatingSystemVersion; */
0x0000, /*WORD MajorImageVersion; */
0x0000, /*WORD MinorImageVersion; */
0x0004, /*WORD MajorSubsystemVersion; */
0x0000, /*WORD MinorSubsystemVersion; */
0x00000000, /*DWORD Win32VersionValue; */
0x00000000, /*DWORD SizeOfImage; */
0x00000200, /*DWORD SizeOfHeaders; */
0x00000000, /*DWORD CheckSum; */
0x0002, /*WORD Subsystem; */
0x0000, /*WORD DllCharacteristics; */
0x00100000, /*DWORD SizeOfStackReserve; */
0x00001000, /*DWORD SizeOfStackCommit; */
0x00100000, /*DWORD SizeOfHeapReserve; */
0x00001000, /*DWORD SizeOfHeapCommit; */
0x00000000, /*DWORD LoaderFlags; */
0x00000010, /*DWORD NumberOfRvaAndSizes; */
/* IMAGE_DATA_DIRECTORY DataDirectory[16]; */
{{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}
}};
struct pe_header pe_header = pe_template;
int i;
FILE *op;
DWORD file_offset, sum;
struct section_info *si;
IMAGE_SECTION_HEADER *psh;
op = fopen(pe->filename, "wb");
if (NULL == op) {
error_noabort("could not write '%s': %s", pe->filename, strerror(errno));
return -1;
}
pe->sizeofheaders = pe_file_align(pe,
sizeof (struct pe_header)
+ pe->sec_count * sizeof (IMAGE_SECTION_HEADER)
);
file_offset = pe->sizeofheaders;
if (2 == pe->s1->verbose)
printf("-------------------------------"
"\n virt file size section" "\n");
for (i = 0; i < pe->sec_count; ++i) {
DWORD addr, size;
const char *sh_name;
si = pe->sec_info + i;
sh_name = si->name;
addr = si->sh_addr - pe->imagebase;
size = si->sh_size;
psh = &si->ish;
if (2 == pe->s1->verbose)
printf("%6x %6x %6x %s\n",
(unsigned)addr, (unsigned)file_offset, (unsigned)size, sh_name);
switch (si->cls) {
case sec_text:
pe_header.opthdr.BaseOfCode = addr;
pe_header.opthdr.AddressOfEntryPoint = addr + pe->start_addr;
break;
case sec_data:
#ifndef TCC_TARGET_X86_64
pe_header.opthdr.BaseOfData = addr;
#endif
break;
case sec_bss:
break;
case sec_reloc:
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size);
break;
case sec_rsrc:
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size);
break;
case sec_pdata:
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXCEPTION, addr, size);
break;
case sec_stab:
break;
}
if (pe->thunk == pe->s1->sections[si->ord]) {
if (pe->imp_size) {
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IMPORT,
pe->imp_offs + addr, pe->imp_size);
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IAT,
pe->iat_offs + addr, pe->iat_size);
}
if (pe->exp_size) {
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXPORT,
pe->exp_offs + addr, pe->exp_size);
}
}
strcpy((char*)psh->Name, sh_name);
psh->Characteristics = pe_sec_flags[si->cls];
psh->VirtualAddress = addr;
psh->Misc.VirtualSize = size;
pe_header.opthdr.SizeOfImage =
umax(pe_virtual_align(pe, size + addr), pe_header.opthdr.SizeOfImage);
if (si->data_size) {
psh->PointerToRawData = file_offset;
file_offset = pe_file_align(pe, file_offset + si->data_size);
psh->SizeOfRawData = file_offset - psh->PointerToRawData;
}
}
//pe_header.filehdr.TimeDateStamp = time(NULL);
pe_header.filehdr.NumberOfSections = pe->sec_count;
pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders;
pe_header.opthdr.ImageBase = pe->imagebase;
if (pe->s1->pe_stack_size)
pe_header.opthdr.SizeOfStackReserve = pe->s1->pe_stack_size;
if (PE_DLL == pe->type)
pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL;
else if (PE_GUI != pe->type)
pe_header.opthdr.Subsystem = 3;
if (pe->subsystem == 9) // WinCE
pe_header.opthdr.Subsystem = 9;
sum = 0;
pe_fwrite(&pe_header, sizeof pe_header, op, &sum);
for (i = 0; i < pe->sec_count; ++i)
pe_fwrite(&pe->sec_info[i].ish, sizeof(IMAGE_SECTION_HEADER), op, &sum);
pe_fpad(op, pe->sizeofheaders);
for (i = 0; i < pe->sec_count; ++i) {
si = pe->sec_info + i;
psh = &si->ish;
if (si->data_size) {
pe_fwrite(si->data, si->data_size, op, &sum);
file_offset = psh->PointerToRawData + psh->SizeOfRawData;
pe_fpad(op, file_offset);
}
}
pe_header.opthdr.CheckSum = sum + file_offset;
fseek(op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET);
pe_fwrite(&pe_header.opthdr.CheckSum, sizeof pe_header.opthdr.CheckSum, op, NULL);
fclose (op);
if (2 == pe->s1->verbose)
printf("-------------------------------\n");
if (pe->s1->verbose)
printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset);
return 0;
}
/*----------------------------------------------------------------------------*/
#if defined(TCC_TARGET_X86_64)
#define ELFW_R_(type) ELF64_R_##type
#define ElfW_Rel ElfW(Rela)
#define REL_TYPE_DIRECT R_X86_64_64
#define R_XXX_THUNKFIX R_X86_64_PC32
#define R_XXX_RELATIVE R_X86_64_RELATIVE
#define ELFW_ST_BIND ELF64_ST_BIND
#define ELFW_ST_TYPE ELF64_ST_TYPE
#define ELFW_ST_INFO ELF64_ST_INFO
#elif defined(TCC_TARGET_I386)
#define ELFW_R_(type) ELF32_R_##type
#define ElfW_Rel ElfW(Rel)
#define REL_TYPE_DIRECT R_386_32
#define R_XXX_THUNKFIX R_386_32
#define R_XXX_RELATIVE R_386_RELATIVE
#define ELFW_ST_BIND ELF32_ST_BIND
#define ELFW_ST_TYPE ELF32_ST_TYPE
#define ELFW_ST_INFO ELF32_ST_INFO
#elif defined(TCC_TARGET_ARM)
#define ELFW_R_(type) ELF32_R_##type
#define ElfW_Rel ElfW(Rel)
#define REL_TYPE_DIRECT R_ARM_ABS32
#define R_XXX_THUNKFIX R_ARM_ABS32
#define R_XXX_RELATIVE R_ARM_RELATIVE
#define ELFW_ST_BIND ELF32_ST_BIND
#define ELFW_ST_TYPE ELF32_ST_TYPE
#define ELFW_ST_INFO ELF32_ST_INFO
#endif
/*----------------------------------------------------------------------------*/
static struct import_symbol *pe_add_import(struct pe_info *pe, int sym_index)
{
int i;
int dll_index;
struct pe_import_info *p;
struct import_symbol *s;
ElfW(Sym) *isym;
isym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index;
dll_index = isym->st_size;
i = dynarray_assoc ((void**)pe->imp_info, pe->imp_count, dll_index);
if (-1 != i) {
p = pe->imp_info[i];
goto found_dll;
}
p = tcc_mallocz(sizeof *p);
p->dll_index = dll_index;
dynarray_add((void***)&pe->imp_info, &pe->imp_count, p);
found_dll:
i = dynarray_assoc ((void**)p->symbols, p->sym_count, sym_index);
if (-1 != i)
return p->symbols[i];
s = tcc_mallocz(sizeof *s);
dynarray_add((void***)&p->symbols, &p->sym_count, s);
s->sym_index = sym_index;
return s;
}
/*----------------------------------------------------------------------------*/
static void pe_build_imports(struct pe_info *pe)
{
int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i;
DWORD rva_base = pe->thunk->sh_addr - pe->imagebase;
int ndlls = pe->imp_count;
for (sym_cnt = i = 0; i < ndlls; ++i)
sym_cnt += pe->imp_info[i]->sym_count;
if (0 == sym_cnt)
return;
pe_align_section(pe->thunk, 16);
pe->imp_offs = dll_ptr = pe->thunk->data_offset;
pe->imp_size = (ndlls + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
pe->iat_offs = dll_ptr + pe->imp_size;
pe->iat_size = (sym_cnt + ndlls) * sizeof(ADDR3264);
section_ptr_add(pe->thunk, pe->imp_size + 2*pe->iat_size);
thk_ptr = pe->iat_offs;
ent_ptr = pe->iat_offs + pe->iat_size;
for (i = 0; i < pe->imp_count; ++i) {
IMAGE_IMPORT_DESCRIPTOR *hdr;
int k, n, dllindex;
ADDR3264 v;
struct pe_import_info *p = pe->imp_info[i];
const char *name;
DLLReference *dllref;
dllindex = p->dll_index;
if (dllindex)
name = (dllref = pe->s1->loaded_dlls[dllindex-1])->name;
else
name = "", dllref = NULL;
/* put the dll name into the import header */
v = put_elf_str(pe->thunk, name);
hdr = (IMAGE_IMPORT_DESCRIPTOR*)(pe->thunk->data + dll_ptr);
hdr->FirstThunk = thk_ptr + rva_base;
hdr->OriginalFirstThunk = ent_ptr + rva_base;
hdr->Name = v + rva_base;
for (k = 0, n = p->sym_count; k <= n; ++k) {
if (k < n) {
int iat_index = p->symbols[k]->iat_index;
int sym_index = p->symbols[k]->sym_index;
ElfW(Sym) *imp_sym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index;
ElfW(Sym) *org_sym = (ElfW(Sym) *)symtab_section->data + iat_index;
const char *name = pe->s1->dynsymtab_section->link->data + imp_sym->st_name;
org_sym->st_value = thk_ptr;
org_sym->st_shndx = pe->thunk->sh_num;
v = pe->thunk->data_offset + rva_base;
section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */
put_elf_str(pe->thunk, name);
#ifdef TCC_IS_NATIVE
if (pe->type == PE_RUN) {
v = imp_sym->st_value;
if (dllref) {
if ( !dllref->handle )
dllref->handle = LoadLibrary(dllref->name);
v = (ADDR3264)GetProcAddress(dllref->handle, name);
}
if (!v)
error_noabort("undefined symbol '%s'", name);
}
#endif
} else {
v = 0; /* last entry is zero */
}
*(ADDR3264*)(pe->thunk->data+thk_ptr) =
*(ADDR3264*)(pe->thunk->data+ent_ptr) = v;
thk_ptr += sizeof (ADDR3264);
ent_ptr += sizeof (ADDR3264);
}
dll_ptr += sizeof(IMAGE_IMPORT_DESCRIPTOR);
dynarray_reset(&p->symbols, &p->sym_count);
}
dynarray_reset(&pe->imp_info, &pe->imp_count);
}
/* ------------------------------------------------------------- */
struct pe_sort_sym
{
int index;
const char *name;
};
static int sym_cmp(const void *va, const void *vb)
{
const char *ca = (*(struct pe_sort_sym**)va)->name;
const char *cb = (*(struct pe_sort_sym**)vb)->name;
return strcmp(ca, cb);
}
static void pe_build_exports(struct pe_info *pe)
{
ElfW(Sym) *sym;
int sym_index, sym_end;
DWORD rva_base, func_o, name_o, ord_o, str_o;
IMAGE_EXPORT_DIRECTORY *hdr;
int sym_count, ord;
struct pe_sort_sym **sorted, *p;
FILE *op;
char buf[MAX_PATH];
const char *dllname;
const char *name;
rva_base = pe->thunk->sh_addr - pe->imagebase;
sym_count = 0, sorted = NULL, op = NULL;
sym_end = symtab_section->data_offset / sizeof(ElfW(Sym));
for (sym_index = 1; sym_index < sym_end; ++sym_index) {
sym = (ElfW(Sym)*)symtab_section->data + sym_index;
name = pe_export_name(pe->s1, sym);
if ((sym->st_other & 1)
/* export only symbols from actually written sections */
&& pe->s1->sections[sym->st_shndx]->sh_addr) {
p = tcc_malloc(sizeof *p);
p->index = sym_index;
p->name = name;
dynarray_add((void***)&sorted, &sym_count, p);
}
#if 0
if (sym->st_other & 1)
printf("export: %s\n", name);
if (sym->st_other & 2)
printf("stdcall: %s\n", name);
#endif
}
if (0 == sym_count)
return;
qsort (sorted, sym_count, sizeof *sorted, sym_cmp);
pe_align_section(pe->thunk, 16);
dllname = tcc_basename(pe->filename);
pe->exp_offs = pe->thunk->data_offset;
func_o = pe->exp_offs + sizeof(IMAGE_EXPORT_DIRECTORY);
name_o = func_o + sym_count * sizeof (DWORD);
ord_o = name_o + sym_count * sizeof (DWORD);
str_o = ord_o + sym_count * sizeof(WORD);
hdr = section_ptr_add(pe->thunk, str_o - pe->exp_offs);
hdr->Characteristics = 0;
hdr->Base = 1;
hdr->NumberOfFunctions = sym_count;
hdr->NumberOfNames = sym_count;
hdr->AddressOfFunctions = func_o + rva_base;
hdr->AddressOfNames = name_o + rva_base;
hdr->AddressOfNameOrdinals = ord_o + rva_base;
hdr->Name = str_o + rva_base;
put_elf_str(pe->thunk, dllname);
#if 1
/* automatically write exports to <output-filename>.def */
strcpy(buf, pe->filename);
strcpy(tcc_fileextension(buf), ".def");
op = fopen(buf, "w");
if (NULL == op) {
error_noabort("could not create '%s': %s", buf, strerror(errno));
} else {
fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname);
if (pe->s1->verbose)
printf("<- %s (%d symbols)\n", buf, sym_count);
}
#endif
for (ord = 0; ord < sym_count; ++ord)
{
p = sorted[ord], sym_index = p->index, name = p->name;
/* insert actual address later in pe_relocate_rva */
put_elf_reloc(symtab_section, pe->thunk,
func_o, R_XXX_RELATIVE, sym_index);
*(DWORD*)(pe->thunk->data + name_o)
= pe->thunk->data_offset + rva_base;
*(WORD*)(pe->thunk->data + ord_o)
= ord;
put_elf_str(pe->thunk, name);
func_o += sizeof (DWORD);
name_o += sizeof (DWORD);
ord_o += sizeof (WORD);
if (op)
fprintf(op, "%s\n", name);
}
pe->exp_size = pe->thunk->data_offset - pe->exp_offs;
dynarray_reset(&sorted, &sym_count);
if (op)
fclose(op);
}
/* ------------------------------------------------------------- */
static void pe_build_reloc (struct pe_info *pe)
{
DWORD offset, block_ptr, addr;
int count, i;
ElfW_Rel *rel, *rel_end;
Section *s = NULL, *sr;
offset = addr = block_ptr = count = i = 0;
rel = rel_end = NULL;
for(;;) {
if (rel < rel_end) {
int type = ELFW_R_(TYPE)(rel->r_info);
addr = rel->r_offset + s->sh_addr;
++ rel;
if (type != REL_TYPE_DIRECT)
continue;
if (count == 0) { /* new block */
block_ptr = pe->reloc->data_offset;
section_ptr_add(pe->reloc, sizeof(struct pe_reloc_header));
offset = addr & 0xFFFFFFFF<<12;
}
if ((addr -= offset) < (1<<12)) { /* one block spans 4k addresses */
WORD *wp = section_ptr_add(pe->reloc, sizeof (WORD));
*wp = addr | IMAGE_REL_BASED_HIGHLOW<<12;
++count;
continue;
}
-- rel;
} else if (i < pe->sec_count) {
sr = (s = pe->s1->sections[pe->sec_info[i++].ord])->reloc;
if (sr) {
rel = (ElfW_Rel *)sr->data;
rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
}
continue;
}
if (count) {
/* store the last block and ready for a new one */
struct pe_reloc_header *hdr;
if (count & 1) /* align for DWORDS */
section_ptr_add(pe->reloc, sizeof(WORD)), ++count;
hdr = (struct pe_reloc_header *)(pe->reloc->data + block_ptr);
hdr -> offset = offset - pe->imagebase;
hdr -> size = count * sizeof(WORD) + sizeof(struct pe_reloc_header);
count = 0;
}
if (rel >= rel_end)
break;
}
}
/* ------------------------------------------------------------- */
static int pe_section_class(Section *s)
{
int type, flags;
const char *name;
type = s->sh_type;
flags = s->sh_flags;
name = s->name;
if (flags & SHF_ALLOC) {
if (type == SHT_PROGBITS) {
if (flags & SHF_EXECINSTR)
return sec_text;
if (flags & SHF_WRITE)
return sec_data;
if (0 == strcmp(name, ".rsrc"))
return sec_rsrc;
if (0 == strcmp(name, ".iedat"))
return sec_idata;
if (0 == strcmp(name, ".pdata"))
return sec_pdata;
return sec_other;
} else if (type == SHT_NOBITS) {
if (flags & SHF_WRITE)
return sec_bss;
}
} else {
if (0 == strcmp(name, ".reloc"))
return sec_reloc;
if (0 == strncmp(name, ".stab", 5)) /* .stab and .stabstr */
return sec_stab;
}
return -1;
}
static int pe_assign_addresses (struct pe_info *pe)
{
int i, k, o, c;
DWORD addr;
int *section_order;
struct section_info *si;
Section *s;
// pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC);
section_order = tcc_malloc(pe->s1->nb_sections * sizeof (int));
for (o = k = 0 ; k < sec_last; ++k) {
for (i = 1; i < pe->s1->nb_sections; ++i) {
s = pe->s1->sections[i];
if (k == pe_section_class(s)) {
// printf("%s %d\n", s->name, k);
s->sh_addr = pe->imagebase;
section_order[o++] = i;
}
}
}
pe->sec_info = tcc_mallocz(o * sizeof (struct section_info));
addr = pe->imagebase + 1;
for (i = 0; i < o; ++i)
{
k = section_order[i];
s = pe->s1->sections[k];
c = pe_section_class(s);
si = &pe->sec_info[pe->sec_count];
#ifdef PE_MERGE_DATA
if (c == sec_bss && pe->sec_count && si[-1].cls == sec_data) {
/* append .bss to .data */
s->sh_addr = addr = ((addr-1) | (s->sh_addralign-1)) + 1;
addr += s->data_offset;
si[-1].sh_size = addr - si[-1].sh_addr;
continue;
}
#endif
strcpy(si->name, s->name);
si->cls = c;
si->ord = k;
si->sh_addr = s->sh_addr = addr = pe_virtual_align(pe, addr);
si->sh_flags = s->sh_flags;
if (c == sec_data && NULL == pe->thunk)
pe->thunk = s;
if (s == pe->thunk) {
pe_build_imports(pe);
pe_build_exports(pe);
}
if (c == sec_reloc)
pe_build_reloc (pe);
if (s->data_offset)
{
if (s->sh_type != SHT_NOBITS) {
si->data = s->data;
si->data_size = s->data_offset;
}
addr += s->data_offset;
si->sh_size = s->data_offset;
++pe->sec_count;
}
// printf("%08x %05x %s\n", si->sh_addr, si->sh_size, si->name);
}
#if 0
for (i = 1; i < pe->s1->nb_sections; ++i) {
Section *s = pe->s1->sections[i];
int type = s->sh_type;
int flags = s->sh_flags;
printf("section %-16s %-10s %5x %s,%s,%s\n",
s->name,
type == SHT_PROGBITS ? "progbits" :
type == SHT_NOBITS ? "nobits" :
type == SHT_SYMTAB ? "symtab" :
type == SHT_STRTAB ? "strtab" :
type == SHT_RELX ? "rel" : "???",
s->data_offset,
flags & SHF_ALLOC ? "alloc" : "",
flags & SHF_WRITE ? "write" : "",
flags & SHF_EXECINSTR ? "exec" : ""
);
}
pe->s1->verbose = 2;
#endif
tcc_free(section_order);
return 0;
}
/* ------------------------------------------------------------- */
static void pe_relocate_rva (struct pe_info *pe, Section *s)
{
Section *sr = s->reloc;
ElfW_Rel *rel, *rel_end;
rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
for(rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++) {
if (ELFW_R_(TYPE)(rel->r_info) == R_XXX_RELATIVE) {
int sym_index = ELFW_R_(SYM)(rel->r_info);
DWORD addr = s->sh_addr;
if (sym_index) {
ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + sym_index;
addr = sym->st_value;
}
// printf("reloc rva %08x %08x %s\n", (DWORD)rel->r_offset, addr, s->name);
*(DWORD*)(s->data + rel->r_offset) += addr - pe->imagebase;
}
}
}
/*----------------------------------------------------------------------------*/
static int pe_isafunc(int sym_index)
{
Section *sr = text_section->reloc;
ElfW_Rel *rel, *rel_end;
Elf32_Word info = ELF32_R_INFO(sym_index, R_386_PC32);
if (!sr)
return 0;
rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
for (rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++)
if (rel->r_info == info)
return 1;
return 0;
}
/*----------------------------------------------------------------------------*/
static int pe_check_symbols(struct pe_info *pe)
{
ElfW(Sym) *sym;
int sym_index, sym_end;
int ret = 0;
pe_align_section(text_section, 8);
sym_end = symtab_section->data_offset / sizeof(ElfW(Sym));
for (sym_index = 1; sym_index < sym_end; ++sym_index) {
sym = (ElfW(Sym) *)symtab_section->data + sym_index;
if (sym->st_shndx == SHN_UNDEF) {
const char *name = symtab_section->link->data + sym->st_name;
unsigned type = ELFW_ST_TYPE(sym->st_info);
int imp_sym = pe_find_import(pe->s1, sym);
struct import_symbol *is;
if (0 == imp_sym)
goto not_found;
if (type == STT_NOTYPE) {
/* symbols from assembler have no type, find out which */
if (pe_isafunc(sym_index))
type = STT_FUNC;
else
type = STT_OBJECT;
}
is = pe_add_import(pe, imp_sym);
if (type == STT_FUNC) {
unsigned long offset = is->thk_offset;
if (offset) {
/* got aliased symbol, like stricmp and _stricmp */
} else {
char buffer[100];
WORD *p;
offset = text_section->data_offset;
/* add the 'jmp IAT[x]' instruction */
#ifdef TCC_TARGET_ARM
p = section_ptr_add(text_section, 8+4); // room for code and address
(*(DWORD*)(p)) = 0xE59FC000; // arm code ldr ip, [pc] ; PC+8+0 = 0001xxxx
(*(DWORD*)(p+2)) = 0xE59CF000; // arm code ldr pc, [ip]
#else
p = section_ptr_add(text_section, 8);
*p = 0x25FF;
#ifdef TCC_TARGET_X86_64
*(DWORD*)(p+1) = (DWORD)-4;
#endif
#endif
/* add a helper symbol, will be patched later in
pe_build_imports */
sprintf(buffer, "IAT.%s", name);
is->iat_index = put_elf_sym(
symtab_section, 0, sizeof(DWORD),
ELFW_ST_INFO(STB_GLOBAL, STT_OBJECT),
0, SHN_UNDEF, buffer);
#ifdef TCC_TARGET_ARM
put_elf_reloc(symtab_section, text_section,
offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position
#else
put_elf_reloc(symtab_section, text_section,
offset + 2, R_XXX_THUNKFIX, is->iat_index);
#endif
is->thk_offset = offset;
}
/* tcc_realloc might have altered sym's address */
sym = (ElfW(Sym) *)symtab_section->data + sym_index;
/* patch the original symbol */
sym->st_value = offset;
sym->st_shndx = text_section->sh_num;
sym->st_other &= ~1; /* do not export */
continue;
}
if (type == STT_OBJECT) { /* data, ptr to that should be */
if (0 == is->iat_index) {
/* original symbol will be patched later in pe_build_imports */
is->iat_index = sym_index;
continue;
}
}
not_found:
error_noabort("undefined symbol '%s'", name);
ret = -1;
} else if (pe->s1->rdynamic
&& ELFW_ST_BIND(sym->st_info) != STB_LOCAL) {
/* if -rdynamic option, then export all non local symbols */
sym->st_other |= 1;
}
}
return ret;
}
/*----------------------------------------------------------------------------*/
#ifdef PE_PRINT_SECTIONS
static void pe_print_section(FILE * f, Section * s)
{
/* just if you'r curious */
BYTE *p, *e, b;
int i, n, l, m;
p = s->data;
e = s->data + s->data_offset;
l = e - p;
fprintf(f, "section \"%s\"", s->name);
if (s->link)
fprintf(f, "\nlink \"%s\"", s->link->name);
if (s->reloc)
fprintf(f, "\nreloc \"%s\"", s->reloc->name);
fprintf(f, "\nv_addr %08X", (unsigned)s->sh_addr);
fprintf(f, "\ncontents %08X", (unsigned)l);
fprintf(f, "\n\n");
if (s->sh_type == SHT_NOBITS)
return;
if (0 == l)
return;
if (s->sh_type == SHT_SYMTAB)
m = sizeof(ElfW(Sym));
else if (s->sh_type == SHT_RELX)
m = sizeof(ElfW_Rel);
else
m = 16;
fprintf(f, "%-8s", "offset");
for (i = 0; i < m; ++i)
fprintf(f, " %02x", i);
n = 56;
if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_RELX) {
const char *fields1[] = {
"name",
"value",
"size",
"bind",
"type",
"other",
"shndx",
NULL
};
const char *fields2[] = {
"offs",
"type",
"symb",
NULL
};
const char **p;
if (s->sh_type == SHT_SYMTAB)
p = fields1, n = 106;
else
p = fields2, n = 58;
for (i = 0; p[i]; ++i)
fprintf(f, "%6s", p[i]);
fprintf(f, " symbol");
}
fprintf(f, "\n");
for (i = 0; i < n; ++i)
fprintf(f, "-");
fprintf(f, "\n");
for (i = 0; i < l;)
{
fprintf(f, "%08X", i);
for (n = 0; n < m; ++n) {
if (n + i < l)
fprintf(f, " %02X", p[i + n]);
else
fprintf(f, " ");
}
if (s->sh_type == SHT_SYMTAB) {
ElfW(Sym) *sym = (ElfW(Sym) *) (p + i);
const char *name = s->link->data + sym->st_name;
fprintf(f, " %04X %04X %04X %02X %02X %02X %04X \"%s\"",
(unsigned)sym->st_name,
(unsigned)sym->st_value,
(unsigned)sym->st_size,
(unsigned)ELFW_ST_BIND(sym->st_info),
(unsigned)ELFW_ST_TYPE(sym->st_info),
(unsigned)sym->st_other,
(unsigned)sym->st_shndx,
name);
} else if (s->sh_type == SHT_RELX) {
ElfW_Rel *rel = (ElfW_Rel *) (p + i);
ElfW(Sym) *sym =
(ElfW(Sym) *) s->link->data + ELFW_R_(SYM)(rel->r_info);
const char *name = s->link->link->data + sym->st_name;
fprintf(f, " %04X %02X %04X \"%s\"",
(unsigned)rel->r_offset,
(unsigned)ELFW_R_(TYPE)(rel->r_info),
(unsigned)ELFW_R_(SYM)(rel->r_info),
name);
} else {
fprintf(f, " ");
for (n = 0; n < m; ++n) {
if (n + i < l) {
b = p[i + n];
if (b < 32 || b >= 127)
b = '.';
fprintf(f, "%c", b);
}
}
}
i += m;
fprintf(f, "\n");
}
fprintf(f, "\n\n");
}
static void pe_print_sections(TCCState *s1, const char *fname)
{
Section *s;
FILE *f;
int i;
f = fopen(fname, "w");
for (i = 1; i < s1->nb_sections; ++i) {
s = s1->sections[i];
pe_print_section(f, s);
}
pe_print_section(f, s1->dynsymtab_section);
fclose(f);
}
#endif
/* ------------------------------------------------------------- */
/* helper function for load/store to insert one more indirection */
ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2)
{
Sym *sym;
ElfW(Sym) *esym;
int r2;
if ((sv->r & (VT_VALMASK|VT_SYM)) != (VT_CONST|VT_SYM) || (sv->r2 != VT_CONST))
return sv;
sym = sv->sym;
if ((sym->type.t & (VT_EXTERN|VT_STATIC)) != VT_EXTERN)
return sv;
if (!sym->c)
put_extern_sym(sym, NULL, 0, 0);
esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
if (!(esym->st_other & 4))
return sv;
// printf("import %04x %04x %04x %s\n", sv->type.t, sym->type.t, sv->r, get_tok_str(sv->sym->v, NULL));
memset(v2, 0, sizeof *v2);
v2->type.t = VT_PTR;
v2->r = VT_CONST | VT_SYM | VT_LVAL;
v2->sym = sv->sym;
r2 = get_reg(RC_INT);
load(r2, v2);
v2->r = r2;
if (sv->c.ui) {
vpushv(v2);
vpushi(sv->c.ui);
gen_opi('+');
*v2 = *vtop--;
}
v2->type.t = sv->type.t;
v2->r |= sv->r & VT_LVAL;
return v2;
}
ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, const void *value)
{
return add_elf_sym(
s1->dynsymtab_section,
(uplong)value,
dllindex, /* st_size */
ELFW_ST_INFO(STB_GLOBAL, STT_NOTYPE),
0,
value ? SHN_ABS : SHN_UNDEF,
name
);
}
static int add_dllref(TCCState *s1, const char *dllname)
{
DLLReference *dllref;
int i;
for (i = 0; i < s1->nb_loaded_dlls; ++i)
if (0 == strcmp(s1->loaded_dlls[i]->name, dllname))
return i + 1;
dllref = tcc_mallocz(sizeof(DLLReference) + strlen(dllname));
strcpy(dllref->name, dllname);
dynarray_add((void ***) &s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
return s1->nb_loaded_dlls;
}
/* ------------------------------------------------------------- */
static int read_mem(FILE *fp, unsigned offset, void *buffer, unsigned len)
{
fseek(fp, offset, 0);
return len == fread(buffer, 1, len, fp);
}
/* -------------------------------------------------------------
* This is for compiled windows resources in 'coff' format
* as generated by 'windres.exe -O coff ...'.
*/
static int pe_load_res(TCCState *s1, FILE *fp)
{
struct pe_rsrc_header hdr;
Section *rsrc_section;
int i, ret = -1;
BYTE *ptr;
unsigned offs;
if (!read_mem(fp, 0, &hdr, sizeof hdr))
goto quit;
if (hdr.filehdr.Machine != 0x014C
|| hdr.filehdr.NumberOfSections != 1
|| strcmp(hdr.sectionhdr.Name, ".rsrc") != 0)
goto quit;
rsrc_section = new_section(s1, ".rsrc", SHT_PROGBITS, SHF_ALLOC);
ptr = section_ptr_add(rsrc_section, hdr.sectionhdr.SizeOfRawData);
offs = hdr.sectionhdr.PointerToRawData;
if (!read_mem(fp, offs, ptr, hdr.sectionhdr.SizeOfRawData))
goto quit;
offs = hdr.sectionhdr.PointerToRelocations;
for (i = 0; i < hdr.sectionhdr.NumberOfRelocations; ++i)
{
struct pe_rsrc_reloc rel;
if (!read_mem(fp, offs, &rel, sizeof rel))
goto quit;
// printf("rsrc_reloc: %x %x %x\n", rel.offset, rel.size, rel.type);
if (rel.type != 7) /* DIR32NB */
goto quit;
put_elf_reloc(symtab_section, rsrc_section,
rel.offset, R_XXX_RELATIVE, 0);
offs += sizeof rel;
}
ret = 0;
quit:
return ret;
}
/* ------------------------------------------------------------- */
static char *trimfront(char *p)
{
while (*p && (unsigned char)*p <= ' ')
++p;
return p;
}
static char *trimback(char *a, char *e)
{
while (e > a && (unsigned char)e[-1] <= ' ')
--e;
*e = 0;;
return a;
}
static char *get_line(char *line, int size, FILE *fp)
{
if (NULL == fgets(line, size, fp))
return NULL;
trimback(line, strchr(line, 0));
return trimfront(line);
}
/* ------------------------------------------------------------- */
static int pe_load_def(TCCState *s1, FILE *fp)
{
int state = 0, ret = -1, dllindex = 0;
char line[400], dllname[80], *p;
for (;;) {
p = get_line(line, sizeof line, fp);
if (NULL == p)
break;
if (0 == *p || ';' == *p)
continue;
switch (state) {
case 0:
if (0 != strnicmp(p, "LIBRARY", 7))
goto quit;
strcpy(dllname, trimfront(p+7));
++state;
continue;
case 1:
if (0 != stricmp(p, "EXPORTS"))
goto quit;
++state;
continue;
case 2:
dllindex = add_dllref(s1, dllname);
++state;
default:
pe_putimport(s1, dllindex, p, NULL);
continue;
}
}
ret = 0;
quit:
return ret;
}
/* ------------------------------------------------------------- */
#define TINY_IMPDEF_GET_EXPORT_NAMES_ONLY
#include "win32/tools/tiny_impdef.c"
static int pe_load_dll(TCCState *s1, const char *dllname, FILE *fp)
{
char *p, *q;
int index;
p = get_export_names(fp);
if (!p)
return -1;
index = add_dllref(s1, dllname);
for (q = p; *q; q += 1 + strlen(q))
pe_putimport(s1, index, q, NULL);
tcc_free(p);
return 0;
}
/* ------------------------------------------------------------- */
ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd)
{
FILE *fp = fdopen(dup(fd), "rb");
int ret = -1;
char buf[10];
if (fp) {
if (0 == strcmp(tcc_fileextension(filename), ".def"))
ret = pe_load_def(s1, fp);
else if (pe_load_res(s1, fp) == 0)
ret = 0;
else if (read_mem(fp, 0, buf, sizeof buf) && 0 == strncmp(buf, "MZ", 2))
ret = pe_load_dll(s1, tcc_basename(filename), fp);
fclose(fp);
}
return ret;
}
ST_FUNC int pe_add_dll(struct TCCState *s, const char *libname)
{
static const char *pat[] = {
"%s.def", "lib%s.def", "%s.dll", "lib%s.dll", NULL
};
const char **p = pat;
do {
char buf[MAX_PATH];
snprintf(buf, sizeof(buf), *p, libname);
if (tcc_add_dll(s, buf, 0) == 0)
return 0;
} while (*++p);
return -1;
}
/* ------------------------------------------------------------- */
#ifdef TCC_TARGET_X86_64
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
{
static const char uw_info[] = {
0x01, // UBYTE: 3 Version , UBYTE: 5 Flags
0x04, // UBYTE Size of prolog
0x02, // UBYTE Count of unwind codes
0x05, // UBYTE: 4 Frame Register (rbp), UBYTE: 4 Frame Register offset (scaled)
// USHORT * n Unwind codes array
// 0x0b, 0x01, 0xff, 0xff, // stack size
0x04, 0x03, // set frame ptr (mov rsp -> rbp)
0x01, 0x50 // push reg (rbp)
};
struct pe_uw *pe_uw = &tcc_state->pe_unwind;
Section *uw, *pd;
WORD *p1;
DWORD *p2;
unsigned o2;
uw = data_section;
pd = pe_uw->pdata;
if (NULL == pd)
{
pe_uw->pdata = pd = find_section(tcc_state, ".pdata");
pe_uw->pdata->sh_addralign = 4;
section_ptr_add(uw, -uw->data_offset & 3);
pe_uw->offs_1 = uw->data_offset;
p1 = section_ptr_add(uw, sizeof uw_info);
/* use one common entry for all functions */
memcpy(p1, uw_info, sizeof uw_info);
pe_uw->sym_1 = put_elf_sym(symtab_section, 0, 0, 0, 0, text_section->sh_num, NULL);
pe_uw->sym_2 = put_elf_sym(symtab_section, 0, 0, 0, 0, uw->sh_num, NULL);
}
o2 = pd->data_offset;
p2 = section_ptr_add(pd, 3 * sizeof (DWORD));
/* record this function */
p2[0] = start;
p2[1] = end;
p2[2] = pe_uw->offs_1;
/* put relocations on it */
put_elf_reloc(symtab_section, pd, o2, R_X86_64_RELATIVE, pe_uw->sym_1);
put_elf_reloc(symtab_section, pd, o2+4, R_X86_64_RELATIVE, pe_uw->sym_1);
put_elf_reloc(symtab_section, pd, o2+8, R_X86_64_RELATIVE, pe_uw->sym_2);
}
#endif
/* ------------------------------------------------------------- */
#ifdef TCC_TARGET_X86_64
#define PE_STDSYM(n,s) n
#else
#define PE_STDSYM(n,s) "_" n s
#endif
static void pe_add_runtime_ex(TCCState *s1, struct pe_info *pe)
{
const char *start_symbol;
unsigned long addr = 0;
int pe_type = 0;
if (find_elf_sym(symtab_section, PE_STDSYM("WinMain","@16")))
pe_type = PE_GUI;
else
if (TCC_OUTPUT_DLL == s1->output_type) {
pe_type = PE_DLL;
/* need this for 'tccelf.c:relocate_section()' */
s1->output_type = TCC_OUTPUT_EXE;
}
else
pe_type = PE_EXE;
start_symbol =
TCC_OUTPUT_MEMORY == s1->output_type
? PE_GUI == pe_type ? "__runwinmain" : "_main"
: PE_DLL == pe_type ? PE_STDSYM("__dllstart","@12")
: PE_GUI == pe_type ? "__winstart" : "__start"
;
if (!s1->leading_underscore || strchr(start_symbol, '@')) {
++start_symbol;
if (start_symbol[0] != '_')
start_symbol = NULL;
}
/* grab the startup code from libtcc1 */
if (start_symbol)
add_elf_sym(symtab_section,
0, 0,
ELFW_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
SHN_UNDEF, start_symbol);
if (0 == s1->nostdlib) {
static const char *libs[] = {
"tcc1", "msvcrt", "kernel32", "", "user32", "gdi32", NULL
};
const char **pp, *p;
for (pp = libs; 0 != (p = *pp); ++pp) {
if (0 == *p) {
if (PE_DLL != pe_type && PE_GUI != pe_type)
break;
} else if (tcc_add_library(s1, p) < 0)
error_noabort("cannot find library: %s", p);
}
}
if (TCC_OUTPUT_MEMORY == s1->output_type)
pe_type = PE_RUN;
if (start_symbol) {
addr = (uplong)tcc_get_symbol_err(s1, start_symbol);
if (PE_RUN == pe_type && addr)
/* for -run GUI's, put '_runwinmain' instead of 'main' */
add_elf_sym(symtab_section,
addr, 0,
ELFW_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0,
text_section->sh_num, "main");
}
pe->type = pe_type;
pe->start_addr = addr;
}
ST_FUNC int pe_output_file(TCCState * s1, const char *filename)
{
int ret;
struct pe_info pe;
int i;
memset(&pe, 0, sizeof pe);
pe.filename = filename;
pe.s1 = s1;
tcc_add_bcheck(s1);
pe_add_runtime_ex(s1, &pe);
relocate_common_syms(); /* assign bss adresses */
tcc_add_linker_symbols(s1);
ret = pe_check_symbols(&pe);
if (ret)
;
else if (filename) {
if (PE_DLL == pe.type) {
pe.reloc = new_section(pe.s1, ".reloc", SHT_PROGBITS, 0);
/* XXX: check if is correct for arm-pe target */
pe.imagebase = 0x10000000;
} else {
#if defined(TCC_TARGET_ARM)
pe.imagebase = 0x00010000;
#else
pe.imagebase = 0x00400000;
#endif
}
/* if no subsystem specified, we use "console" subsystem by default */
if (s1->pe_subsystem != 0)
pe.subsystem = s1->pe_subsystem;
else
#if defined(TCC_TARGET_ARM)
pe.subsystem = 9;
#else
pe.subsystem = 3;
#endif
/* set default file/section alignment */
if (pe.subsystem == 1) {
pe.section_align = 0x20;
pe.file_align = 0x20;
} else {
pe.section_align = 0x1000;
pe.file_align = 0x200;
}
if (s1->section_align != 0)
pe.section_align = s1->section_align;
if (s1->pe_file_align != 0)
pe.file_align = s1->pe_file_align;
if ((pe.subsystem >= 10) && (pe.subsystem <= 12))
pe.imagebase = 0;
if (s1->has_text_addr)
pe.imagebase = s1->text_addr;
pe_assign_addresses(&pe);
relocate_syms(s1, 0);
for (i = 1; i < s1->nb_sections; ++i) {
Section *s = s1->sections[i];
if (s->reloc) {
relocate_section(s1, s);
pe_relocate_rva(&pe, s);
}
}
if (s1->nb_errors)
ret = -1;
else
ret = pe_write(&pe);
tcc_free(pe.sec_info);
} else {
#ifndef TCC_IS_NATIVE
error_noabort("-run supported only on native platform");
#endif
pe.thunk = data_section;
pe_build_imports(&pe);
}
#ifdef PE_PRINT_SECTIONS
pe_print_sections(s1, "tcc.log");
#endif
return ret;
}
/* ------------------------------------------------------------- */