1,7 → 1,7 |
/* |
* TCCPE.C - PE file output for the TinyC Compiler |
* |
* Copyright (c) 2005 grischka |
* 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 |
18,16 → 18,70 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
|
typedef unsigned char BYTE; |
typedef unsigned short WORD; |
typedef unsigned long DWORD; |
#define ST static |
#include "tcc.h" |
|
/* XXX: move that to TCC ? */ |
int verbose = 0; |
#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 |
# 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 IMAGE_FILE_MACHINE 0x8664 |
# define RSRC_RELTYPE 3 |
|
#elif defined TCC_TARGET_ARM |
# define ADDR3264 DWORD |
# define REL_TYPE_DIRECT R_ARM_ABS32 |
# define R_XXX_THUNKFIX R_ARM_ABS32 |
# define R_XXX_RELATIVE R_ARM_RELATIVE |
# define IMAGE_FILE_MACHINE 0x01C0 |
# define RSRC_RELTYPE 7 /* ??? (not tested) */ |
|
#elif defined TCC_TARGET_I386 |
# define ADDR3264 DWORD |
# define REL_TYPE_DIRECT R_386_32 |
# define R_XXX_THUNKFIX R_386_32 |
# define R_XXX_RELATIVE R_386_RELATIVE |
# define IMAGE_FILE_MACHINE 0x014C |
# define RSRC_RELTYPE 7 /* DIR32NB */ |
|
#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 */ |
48,8 → 102,6 |
WORD e_oeminfo; /* OEM information; e_oemid specific */ |
WORD e_res2[10]; /* Reserved words */ |
DWORD e_lfanew; /* File address of new exe header */ |
BYTE e_code[0x40]; |
|
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; |
|
#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ |
84,10 → 136,11 |
DWORD SizeOfUninitializedData; |
DWORD AddressOfEntryPoint; |
DWORD BaseOfCode; |
#ifndef TCC_TARGET_X86_64 |
DWORD BaseOfData; |
|
#endif |
/* NT additional fields. */ |
DWORD ImageBase; |
ADDR3264 ImageBase; |
DWORD SectionAlignment; |
DWORD FileAlignment; |
WORD MajorOperatingSystemVersion; |
102,17 → 155,15 |
DWORD CheckSum; |
WORD Subsystem; |
WORD DllCharacteristics; |
DWORD SizeOfStackReserve; |
DWORD SizeOfStackCommit; |
DWORD SizeOfHeapReserve; |
DWORD SizeOfHeapCommit; |
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; |
|
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; |
|
|
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */ |
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */ |
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */ |
151,7 → 202,31 |
|
#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; |
169,172 → 244,102 |
#define IMAGE_REL_BASED_SECTION 6 |
#define IMAGE_REL_BASED_REL32 7 |
|
/* ----------------------------------------------------------- */ |
#pragma pack(pop) |
|
/* ----------------------------------------------------------- */ |
IMAGE_DOS_HEADER pe_dos_hdr = { |
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 */ |
#endif /* ndef IMAGE_NT_SIGNATURE */ |
/* ----------------------------------------------------------- */ |
#pragma pack(push, 1) |
|
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 */ |
{ /* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 0x00 */ |
/*0040 */ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, |
0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, |
/*0050 */ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, |
0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, |
/*0060 */ 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, |
0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, |
/*0070 */ 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
/*0080 */ |
} |
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 |
}; |
|
DWORD pe_magic = IMAGE_NT_SIGNATURE; |
|
IMAGE_FILE_HEADER pe_file_hdr = { |
0x014C, /*WORD Machine; */ |
0x0003, /*WORD NumberOfSections; */ |
0x00000000, /*DWORD TimeDateStamp; */ |
0x00000000, /*DWORD PointerToSymbolTable; */ |
0x00000000, /*DWORD NumberOfSymbols; */ |
0x00E0, /*WORD SizeOfOptionalHeader; */ |
0x030F /*WORD Characteristics; */ |
struct pe_reloc_header { |
DWORD offset; |
DWORD size; |
}; |
|
IMAGE_OPTIONAL_HEADER32 pe_opt_hdr = { |
/* Standard fields. */ |
0x010B, /*WORD Magic; */ |
0x06, /*BYTE MajorLinkerVersion; */ |
0x00, /*BYTE MinorLinkerVersion; */ |
0x00000000, /*DWORD SizeOfCode; */ |
0x00000000, /*DWORD SizeOfInitializedData; */ |
0x00000000, /*DWORD SizeOfUninitializedData; */ |
0x00000000, /*DWORD AddressOfEntryPoint; */ |
0x00000000, /*DWORD BaseOfCode; */ |
0x00000000, /*DWORD BaseOfData; */ |
|
/* NT additional fields. */ |
0x00400000, /*DWORD ImageBase; */ |
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_rsrc_header { |
struct _IMAGE_FILE_HEADER filehdr; |
struct _IMAGE_SECTION_HEADER sectionhdr; |
}; |
|
/*----------------------------------------------------------------------------*/ |
|
/*----------------------------------------------------------------------------*/ |
|
struct pe_import_header { |
DWORD first_entry; |
DWORD time_date; |
DWORD forwarder; |
DWORD lib_name_offset; |
DWORD first_thunk; |
}; |
|
struct pe_export_header { |
DWORD Characteristics; |
DWORD TimeDateStamp; |
DWORD Version; |
DWORD Name; |
DWORD Base; |
DWORD NumberOfFunctions; |
DWORD NumberOfNames; |
DWORD AddressOfFunctions; |
DWORD AddressOfNames; |
DWORD AddressOfNameOrdinals; |
}; |
|
struct pe_reloc_header { |
struct pe_rsrc_reloc { |
DWORD offset; |
DWORD size; |
WORD type; |
}; |
|
#pragma pack(pop) |
|
/* ------------------------------------------------------------- */ |
/* internal temporary structures */ |
|
ST const char *pe_sec_names[] = { |
".text", |
".data", |
".bss", |
".rsrc", |
".reloc", |
".stab", |
".stabstr" |
}; |
/* |
#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_stab, |
sec_stabstr, |
pe_sec_number |
sec_last |
}; |
|
ST DWORD pe_flags[] = { |
static const DWORD pe_sec_flags[] = { |
0x60000020, /* ".text", */ |
0xC0000040, /* ".data", */ |
0xC0000080, /* ".bss", */ |
0x40000040, /* ".idata" , */ |
0x40000040, /* ".pdata" , */ |
0xE0000060, /* < other > , */ |
0x40000040, /* ".rsrc", */ |
0x42000802, /* ".stab" , */ |
0x42000040, /* ".reloc", */ |
0x42000802, /* ".stab", */ |
0x42000802 /* ".stabstr", */ |
}; |
|
struct section_info { |
struct section_info *next; |
int id; |
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 offset; |
int iat_index; |
int thk_offset; |
}; |
|
struct pe_import_info { |
344,9 → 349,13 |
}; |
|
struct pe_info { |
TCCState *s1; |
Section *reloc; |
Section *thunk; |
const char *filename; |
int type; |
DWORD sizeofheaders; |
DWORD imagebase; |
ADDR3264 imagebase; |
DWORD start_addr; |
DWORD imp_offs; |
DWORD imp_size; |
354,104 → 363,67 |
DWORD iat_size; |
DWORD exp_offs; |
DWORD exp_size; |
struct section_info sh_info[pe_sec_number]; |
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; |
Section *reloc; |
Section *thunk; |
TCCState *s1; |
}; |
|
/* ------------------------------------------------------------- */ |
#define PE_MERGE_DATA |
// #define PE_PRINT_SECTIONS |
|
#ifndef MAX_PATH |
#define MAX_PATH 260 |
#endif |
|
void error_noabort(const char *, ...); |
|
ST char pe_type; |
|
#define PE_NUL 0 |
#define PE_DLL 1 |
#define PE_GUI 2 |
#define PE_EXE 3 |
#define PE_RUN 4 |
|
ST int pe_find_import(TCCState * s1, const char *symbol, char *ret) |
/* --------------------------------------------*/ |
|
static const char *pe_export_name(TCCState *s1, ElfW(Sym) *sym) |
{ |
int sym_index = find_elf_sym(s1->dynsymtab_section, symbol); |
if (0 == sym_index) { |
/* Hm, maybe it's '_symbol' instead of 'symbol' or '__imp__symbol' */ |
char buffer[100]; |
if (0 == memcmp(symbol, "__imp__", 7)) |
symbol += 6; |
else |
buffer[0] = '_', strcpy(buffer + 1, symbol), symbol = buffer; |
sym_index = find_elf_sym(s1->dynsymtab_section, symbol); |
const char *name = symtab_section->link->data + sym->st_name; |
if (s1->leading_underscore && name[0] == '_' && !(sym->st_other & ST_PE_STDCALL)) |
return name + 1; |
return name; |
} |
if (ret) |
strcpy(ret, symbol); |
return sym_index; |
} |
|
#ifdef WIN32 |
ST void **pe_imp; |
ST int nb_pe_imp; |
|
void *resolve_sym(struct TCCState *s1, const char *symbol, int type) |
static int pe_find_import(TCCState * s1, ElfW(Sym) *sym) |
{ |
char buffer[100], *p = buffer; |
void *a = NULL; |
int sym_index = pe_find_import(s1, symbol, p); |
int dll_index; |
const char *dll_name; |
void *hm; |
char buffer[200]; |
const char *s, *p; |
int sym_index = 0, n = 0; |
|
if (sym_index) { |
dll_index = ((Elf32_Sym *) s1->dynsymtab_section->data)[sym_index]. |
st_other; |
dll_name = s1->loaded_dlls[dll_index]->name; |
hm = GetModuleHandleA(dll_name); |
if (NULL == hm) |
hm = LoadLibraryA(dll_name); |
if (hm) { |
a = GetProcAddress(hm, buffer); |
if (a && STT_OBJECT == type) { |
// need to return a pointer to the address for data objects |
dynarray_add(&pe_imp, &nb_pe_imp, a); |
a = &pe_imp[nb_pe_imp - 1]; |
do { |
s = pe_export_name(s1, sym); |
if (n) { |
/* second try: */ |
if (sym->st_other & ST_PE_STDCALL) { |
/* 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; |
} |
return a; |
} |
#endif |
|
#define for_sym_in_symtab(sym) \ |
for (sym = (Elf32_Sym *)symtab_section->data + 1; \ |
sym < (Elf32_Sym *)(symtab_section->data + \ |
symtab_section->data_offset); \ |
++sym) |
|
#define pe_set_datadir(dir,addr,size) \ |
pe_opt_hdr.DataDirectory[dir].VirtualAddress = addr, \ |
pe_opt_hdr.DataDirectory[dir].Size = size |
|
/*----------------------------------------------------------------------------*/ |
ST void dynarray_reset(void ***pp, int *n) |
{ |
int i; |
for (i = 0; i < *n; ++i) |
tcc_free((*pp)[i]); |
tcc_free(*pp); |
*pp = NULL; |
*n = 0; |
} |
|
ST int dynarray_assoc(void **pp, int n, int key) |
static int dynarray_assoc(void **pp, int n, int key) |
{ |
int i; |
for (i = 0; i < n; ++i, ++pp) |
461,103 → 433,216 |
} |
|
#if 0 |
ST DWORD umin(DWORD a, DWORD b) |
ST_FN DWORD umin(DWORD a, DWORD b) |
{ |
return a < b ? a : b; |
} |
#endif |
|
ST DWORD umax(DWORD a, DWORD b) |
static DWORD umax(DWORD a, DWORD b) |
{ |
return a < b ? b : a; |
} |
|
ST void pe_fpad(FILE * fp, DWORD new_pos) |
static DWORD pe_file_align(struct pe_info *pe, DWORD n) |
{ |
DWORD pos = ftell(fp); |
while (++pos <= new_pos) |
fputc(0, fp); |
return (n + (pe->file_align - 1)) & ~(pe->file_align - 1); |
} |
|
ST DWORD pe_file_align(DWORD n) |
static DWORD pe_virtual_align(struct pe_info *pe, DWORD n) |
{ |
return (n + (0x200 - 1)) & ~(0x200 - 1); |
return (n + (pe->section_align - 1)) & ~(pe->section_align - 1); |
} |
|
ST DWORD pe_virtual_align(DWORD n) |
static void pe_align_section(Section *s, int a) |
{ |
return (n + (0x1000 - 1)) & ~(0x1000 - 1); |
} |
|
ST 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); |
} |
|
/*----------------------------------------------------------------------------*/ |
ST int pe_write_pe(struct pe_info *pe) |
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 */ |
IMAGE_FILE_MACHINE, /*WORD Machine; */ |
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; |
IMAGE_SECTION_HEADER ish[pe_sec_number], *psh; |
int sec_index = 0; |
DWORD file_offset, sum; |
struct section_info *si; |
IMAGE_SECTION_HEADER *psh; |
|
op = fopen(pe->filename, "wb"); |
if (NULL == op) { |
error_noabort("could not create file: %s", pe->filename); |
return 1; |
tcc_error_noabort("could not write '%s': %s", pe->filename, strerror(errno)); |
return -1; |
} |
|
memset(&ish, 0, sizeof ish); |
|
pe->sizeofheaders = pe_file_align(sizeof pe_dos_hdr |
+ sizeof pe_magic |
+ sizeof pe_file_hdr |
+ sizeof pe_opt_hdr |
+ |
pe->sec_count * |
sizeof(IMAGE_SECTION_HEADER) |
pe->sizeofheaders = pe_file_align(pe, |
sizeof (struct pe_header) |
+ pe->sec_count * sizeof (IMAGE_SECTION_HEADER) |
); |
|
file_offset = pe->sizeofheaders; |
pe_fpad(op, file_offset); |
|
if (2 == verbose) |
if (2 == pe->s1->verbose) |
printf("-------------------------------" |
"\n virt file size section" "\n"); |
|
for (i = 0; i < pe->sec_count; ++i) { |
struct section_info *si = pe->sh_info + i; |
const char *sh_name = pe_sec_names[si->id]; |
unsigned long addr = si->sh_addr - pe->imagebase; |
unsigned long size = si->sh_size; |
DWORD addr, size; |
const char *sh_name; |
|
if (2 == verbose) |
printf("%6lx %6lx %6lx %s\n", |
addr, file_offset, size, sh_name); |
si = pe->sec_info + i; |
sh_name = si->name; |
addr = si->sh_addr - pe->imagebase; |
size = si->sh_size; |
psh = &si->ish; |
|
switch (si->id) { |
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_opt_hdr.BaseOfCode = addr; |
pe_opt_hdr.AddressOfEntryPoint = addr + pe->start_addr; |
pe_header.opthdr.BaseOfCode = addr; |
pe_header.opthdr.AddressOfEntryPoint = addr + pe->start_addr; |
break; |
|
case sec_data: |
pe_opt_hdr.BaseOfData = addr; |
if (pe->imp_size) { |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_IMPORT, |
pe->imp_offs + addr, pe->imp_size); |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_IAT, |
pe->iat_offs + addr, pe->iat_size); |
} |
if (pe->exp_size) { |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_EXPORT, |
pe->exp_offs + addr, pe->exp_size); |
} |
#ifndef TCC_TARGET_X86_64 |
pe_header.opthdr.BaseOfData = addr; |
#endif |
break; |
|
case sec_bss: |
564,77 → 649,100 |
break; |
|
case sec_reloc: |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size); |
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size); |
break; |
|
case sec_rsrc: |
pe_set_datadir(IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size); |
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size); |
break; |
|
case sec_stab: |
case sec_pdata: |
pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXCEPTION, addr, size); |
break; |
|
case sec_stabstr: |
case sec_stab: |
break; |
} |
|
psh = &ish[sec_index++]; |
strcpy((char *) psh->Name, sh_name); |
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); |
} |
} |
|
psh->Characteristics = pe_flags[si->id]; |
strncpy((char*)psh->Name, sh_name, sizeof psh->Name); |
|
psh->Characteristics = pe_sec_flags[si->cls]; |
psh->VirtualAddress = addr; |
psh->Misc.VirtualSize = size; |
pe_opt_hdr.SizeOfImage = |
umax(psh->VirtualAddress + psh->Misc.VirtualSize, |
pe_opt_hdr.SizeOfImage); |
pe_header.opthdr.SizeOfImage = |
umax(pe_virtual_align(pe, size + addr), pe_header.opthdr.SizeOfImage); |
|
if (si->data_size) { |
psh->PointerToRawData = file_offset; |
fwrite(si->data, 1, si->data_size, op); |
file_offset = pe_file_align(file_offset + si->data_size); |
file_offset = pe_file_align(pe, file_offset + si->data_size); |
psh->SizeOfRawData = file_offset - psh->PointerToRawData; |
pe_fpad(op, file_offset); |
} |
} |
|
/*----------------------------------------------------- */ |
//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; |
pe_header.opthdr.Subsystem = pe->subsystem; |
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; |
|
pe_file_hdr.NumberOfSections = sec_index; |
pe_opt_hdr.SizeOfHeaders = pe->sizeofheaders; |
pe_opt_hdr.ImageBase = pe->imagebase; |
if (PE_DLL == pe_type) |
pe_file_hdr.Characteristics = 0x230E; |
else if (PE_GUI != pe_type) |
pe_opt_hdr.Subsystem = 3; |
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); |
} |
} |
|
fseek(op, SEEK_SET, 0); |
fwrite(&pe_dos_hdr, 1, sizeof pe_dos_hdr, op); |
fwrite(&pe_magic, 1, sizeof pe_magic, op); |
fwrite(&pe_file_hdr, 1, sizeof pe_file_hdr, op); |
fwrite(&pe_opt_hdr, 1, sizeof pe_opt_hdr, op); |
for (i = 0; i < sec_index; ++i) |
fwrite(&ish[i], 1, sizeof(IMAGE_SECTION_HEADER), op); |
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 == verbose) |
if (2 == pe->s1->verbose) |
printf("-------------------------------\n"); |
if (verbose) |
printf("<-- %s (%lu bytes)\n", pe->filename, file_offset); |
if (pe->s1->verbose) |
printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset); |
|
return 0; |
} |
|
/*----------------------------------------------------------------------------*/ |
ST int pe_add_import(struct pe_info *pe, int sym_index, DWORD offset) |
|
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; |
|
dll_index = |
((Elf32_Sym *) pe->s1->dynsymtab_section->data)[sym_index]. |
st_other; |
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]; |
647,21 → 755,19 |
found_dll: |
i = dynarray_assoc((void **) p->symbols, p->sym_count, sym_index); |
if (-1 != i) |
goto found_sym; |
return p->symbols[i]; |
|
s = tcc_mallocz(sizeof *s); |
dynarray_add((void***)&p->symbols, &p->sym_count, s); |
s->sym_index = sym_index; |
s->offset = offset; |
dynarray_add((void ***) &p->symbols, &p->sym_count, s); |
|
found_sym: |
return 1; |
return s; |
} |
|
/*----------------------------------------------------------------------------*/ |
ST void pe_build_imports(struct pe_info *pe) |
static void pe_build_imports(struct pe_info *pe) |
{ |
int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i; |
DWORD voffset = pe->thunk->sh_addr - pe->imagebase; |
DWORD rva_base = pe->thunk->sh_addr - pe->imagebase; |
int ndlls = pe->imp_count; |
|
for (sym_cnt = i = 0; i < ndlls; ++i) |
673,161 → 779,217 |
pe_align_section(pe->thunk, 16); |
|
pe->imp_offs = dll_ptr = pe->thunk->data_offset; |
pe->imp_size = (ndlls + 1) * sizeof(struct pe_import_header); |
pe->imp_size = (ndlls + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR); |
pe->iat_offs = dll_ptr + pe->imp_size; |
pe->iat_size = (sym_cnt + ndlls) * sizeof(DWORD); |
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) { |
struct pe_import_header *hdr; |
int k, n, v; |
IMAGE_IMPORT_DESCRIPTOR *hdr; |
int k, n, dllindex; |
ADDR3264 v; |
struct pe_import_info *p = pe->imp_info[i]; |
const char *name = pe->s1->loaded_dlls[p->dll_index]->name; |
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 */ |
if (0 == strncmp(name, "lib", 3)) |
name += 3; |
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; |
|
hdr = (struct pe_import_header *) (pe->thunk->data + dll_ptr); |
hdr->first_thunk = thk_ptr + voffset; |
hdr->first_entry = ent_ptr + voffset; |
hdr->lib_name_offset = v + voffset; |
|
for (k = 0, n = p->sym_count; k <= n; ++k) { |
if (k < n) { |
DWORD offset = p->symbols[k]->offset; |
int iat_index = p->symbols[k]->iat_index; |
int sym_index = p->symbols[k]->sym_index; |
Elf32_Sym *sym = |
(Elf32_Sym *) pe->s1->dynsymtab_section->data + |
sym_index; |
const char *name = |
pe->s1->dynsymtab_section->link->data + sym->st_name; |
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; |
int ordinal; |
|
if (offset & 0x80000000) { /* ref to data */ |
Elf32_Sym *sym = |
&((Elf32_Sym *) symtab_section-> |
data)[offset & 0x7FFFFFFF]; |
sym->st_value = thk_ptr; |
sym->st_shndx = pe->thunk->sh_num; |
} else { /* ref to function */ |
org_sym->st_value = thk_ptr; |
org_sym->st_shndx = pe->thunk->sh_num; |
|
char buffer[100]; |
sprintf(buffer, "IAT.%s", name); |
sym_index = |
put_elf_sym(symtab_section, thk_ptr, sizeof(DWORD), |
ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), |
0, pe->thunk->sh_num, buffer); |
if (dllref) |
v = 0, ordinal = imp_sym->st_value; /* ordinal from pe_load_def */ |
else |
ordinal = 0, v = imp_sym->st_value; /* address from tcc_add_symbol() */ |
|
put_elf_reloc(symtab_section, text_section, offset, R_386_32, /*R_JMP_SLOT, */ |
sym_index); |
#ifdef TCC_IS_NATIVE |
if (pe->type == PE_RUN) { |
if (dllref) { |
if ( !dllref->handle ) |
dllref->handle = LoadLibrary(dllref->name); |
v = (ADDR3264)GetProcAddress(dllref->handle, ordinal?(LPCSTR)NULL+ordinal:name); |
} |
v = pe->thunk->data_offset + voffset; |
if (!v) |
tcc_error_noabort("can't build symbol '%s'", name); |
} else |
#endif |
if (ordinal) { |
v = ordinal | (ADDR3264)1 << (sizeof(ADDR3264)*8 - 1); |
} else { |
v = pe->thunk->data_offset + rva_base; |
section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */ |
put_elf_str(pe->thunk, name); |
} |
|
} else { |
v = 0; // last entry is zero |
v = 0; /* last entry is zero */ |
} |
*(DWORD *) (pe->thunk->data + thk_ptr) = |
*(DWORD *) (pe->thunk->data + ent_ptr) = v; |
thk_ptr += sizeof(DWORD); |
ent_ptr += sizeof(DWORD); |
|
*(ADDR3264*)(pe->thunk->data+thk_ptr) = |
*(ADDR3264*)(pe->thunk->data+ent_ptr) = v; |
thk_ptr += sizeof (ADDR3264); |
ent_ptr += sizeof (ADDR3264); |
} |
dll_ptr += sizeof(struct pe_import_header); |
dynarray_reset((void ***) &p->symbols, &p->sym_count); |
dll_ptr += sizeof(IMAGE_IMPORT_DESCRIPTOR); |
dynarray_reset(&p->symbols, &p->sym_count); |
} |
dynarray_reset((void ***) &pe->imp_info, &pe->imp_count); |
dynarray_reset(&pe->imp_info, &pe->imp_count); |
} |
|
/* ------------------------------------------------------------- */ |
ST int sym_cmp(const void *va, const void *vb) |
|
struct pe_sort_sym |
{ |
Elf32_Sym *sa = (Elf32_Sym *)symtab_section->data + *(int*)va; |
Elf32_Sym *sb = (Elf32_Sym *)symtab_section->data + *(int*)vb; |
const char *ca = symtab_section->link->data + sa->st_name; |
const char *cb = symtab_section->link->data + sb->st_name; |
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); |
} |
|
ST void pe_build_exports(struct pe_info *pe) |
static void pe_build_exports(struct pe_info *pe) |
{ |
Elf32_Sym *sym; |
DWORD func_offset, voffset; |
struct pe_export_header *hdr; |
int sym_count, n, ord, *sorted; |
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; |
|
voffset = pe->thunk->sh_addr - pe->imagebase; |
sym_count = 0, n = 1, sorted = NULL; |
FILE *op; |
char buf[MAX_PATH]; |
const char *dllname; |
const char *name; |
|
// for simplicity only functions are exported |
for_sym_in_symtab(sym) |
{ |
if ((sym->st_other & 1) |
&& sym->st_shndx == text_section->sh_num) |
dynarray_add((void***)&sorted, &sym_count, (void*)n); |
++n; |
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 & ST_PE_EXPORT) |
/* 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 & ST_PE_EXPORT) |
printf("export: %s\n", name); |
if (sym->st_other & ST_PE_STDCALL) |
printf("stdcall: %s\n", name); |
#endif |
} |
|
if (0 == sym_count) |
return; |
|
qsort (sorted, sym_count, sizeof sorted[0], sym_cmp); |
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; |
hdr = section_ptr_add(pe->thunk, |
sizeof(struct pe_export_header) + |
sym_count * (2 * sizeof(DWORD) + sizeof(WORD))); |
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); |
|
func_offset = pe->exp_offs + sizeof(struct pe_export_header); |
|
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_offset + voffset; |
hdr->AddressOfNames = hdr->AddressOfFunctions + sym_count * sizeof(DWORD); |
hdr->AddressOfNameOrdinals = hdr->AddressOfNames + sym_count * sizeof(DWORD); |
hdr->Name = pe->thunk->data_offset + voffset; |
put_elf_str(pe->thunk, tcc_basename(pe->filename)); |
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 */ |
pstrcpy(buf, sizeof buf, pe->filename); |
strcpy(tcc_fileextension(buf), ".def"); |
op = fopen(buf, "w"); |
if (NULL == op) { |
tcc_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) |
{ |
char *name; DWORD *p, *pfunc, *pname; WORD *pord; |
sym = (Elf32_Sym *)symtab_section->data + sorted[ord]; |
name = symtab_section->link->data + sym->st_name; |
p = (DWORD*)(pe->thunk->data + func_offset); |
pfunc = p + ord; |
pname = p + sym_count + ord; |
pord = (WORD *)(p + 2*sym_count) + ord; |
*pfunc = sym->st_value + pe->s1->sections[sym->st_shndx]->sh_addr - pe->imagebase; |
*pname = pe->thunk->data_offset + voffset; |
*pord = 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); |
/* printf("export: %s\n", 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; |
tcc_free(sorted); |
dynarray_reset(&sorted, &sym_count); |
if (op) |
fclose(op); |
} |
|
/* ------------------------------------------------------------- */ |
ST void pe_build_reloc(struct pe_info *pe, int *section_order, |
int section_count) |
static void pe_build_reloc (struct pe_info *pe) |
{ |
DWORD offset, block_ptr, addr; |
int count, i; |
Elf32_Rel *rel, *rel_end; |
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 = ELF32_R_TYPE(rel->r_info); |
int type = ELFW(R_TYPE)(rel->r_info); |
addr = rel->r_offset + s->sh_addr; |
++rel; |
if (type != R_386_32) |
if (type != REL_TYPE_DIRECT) |
continue; |
if (count == 0) { /* new block */ |
block_ptr = pe->reloc->data_offset; |
841,25 → 1003,27 |
continue; |
} |
--rel; |
} else if (i < section_count) { |
sr = (s = pe->s1->sections[section_order[i++]])->reloc; |
|
} else if (i < pe->sec_count) { |
sr = (s = pe->s1->sections[pe->sec_info[i++].ord])->reloc; |
if (sr) { |
rel = (Elf32_Rel *) sr->data; |
rel_end = (Elf32_Rel *) (sr->data + sr->data_offset); |
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 */ |
if (count) { |
/* store the last block and ready for a new one */ |
struct pe_reloc_header *hdr; |
if (count & 1) |
section_ptr_add(pe->reloc, 2), ++count; |
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); |
hdr -> size = count * sizeof(WORD) + sizeof(struct pe_reloc_header); |
count = 0; |
} |
|
if (rel >= rel_end) |
break; |
} |
866,48 → 1030,104 |
} |
|
/* ------------------------------------------------------------- */ |
ST int pe_assign_addresses(struct pe_info *pe) |
static int pe_section_class(Section *s) |
{ |
int i, k, n; |
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[pe_sec_number]; |
struct section_info *si_data = NULL; |
int *section_order; |
struct section_info *si; |
Section *s; |
|
pe->imagebase = PE_DLL == pe_type ? 0x10000000 : 0x00400000; |
// 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; |
|
if (PE_DLL == pe_type) |
pe->reloc = new_section(pe->s1, ".reloc", SHT_DYNAMIC, SHF_ALLOC); |
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]; |
|
for (n = k = 0; n < pe_sec_number; ++n) { |
for (i = 1; i < pe->s1->nb_sections; ++i) { |
Section *s = pe->s1->sections[i]; |
if (0 == strcmp(s->name, pe_sec_names[n])) { |
struct section_info *si = &pe->sh_info[pe->sec_count]; |
#ifdef PE_MERGE_DATA |
if (n == sec_bss && si_data) { |
if (c == sec_bss && pe->sec_count && si[-1].cls == sec_data) { |
/* append .bss to .data */ |
s->sh_addr = addr = ((addr - 1) | 15) + 1; |
s->sh_addr = addr = ((addr-1) | (s->sh_addralign-1)) + 1; |
addr += s->data_offset; |
si_data->sh_size = addr - si_data->sh_addr; |
} else |
si[-1].sh_size = addr - si[-1].sh_addr; |
continue; |
} |
#endif |
{ |
si->sh_addr = s->sh_addr = addr = |
pe_virtual_align(addr); |
si->id = n; |
if (c == sec_stab && 0 == pe->s1->do_debug) |
continue; |
|
if (n == sec_data) { |
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; |
si_data = si; |
|
if (s == pe->thunk) { |
pe_build_imports(pe); |
pe_build_exports(pe); |
} else if (n == sec_reloc) { |
pe_build_reloc(pe, section_order, k); |
} |
|
if (s->data_offset) { |
if (n != sec_bss) { |
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; |
} |
916,55 → 1136,168 |
si->sh_size = s->data_offset; |
++pe->sec_count; |
} |
//printf("Section %08X %04X %s\n", si->sh_addr, si->data_size, s->name); |
// printf("%08x %05x %s\n", si->sh_addr, si->sh_size, si->name); |
} |
section_order[k] = i, ++k; |
|
#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; |
} |
|
/*----------------------------------------------------------------------------*/ |
ST int pe_check_symbols(struct pe_info *pe) |
static int pe_check_symbols(struct pe_info *pe) |
{ |
Elf32_Sym *sym; |
ElfW(Sym) *sym; |
int sym_index, sym_end; |
int ret = 0; |
|
pe_align_section(text_section, 8); |
|
for_sym_in_symtab(sym) { |
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 *symbol = symtab_section->link->data + sym->st_name; |
unsigned type = ELF32_ST_TYPE(sym->st_info); |
int sym_index = pe_find_import(pe->s1, symbol, NULL); |
if (sym_index) { |
|
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 = text_section->data_offset; |
if (pe_add_import(pe, sym_index, offset + 2)) { |
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 */ |
*(WORD *) section_ptr_add(text_section, 8) = |
0x25FF; |
/* patch the symbol */ |
#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_value = offset; |
sym->st_other &= ~ST_PE_EXPORT; /* do not export */ |
continue; |
} |
} else if (type == STT_OBJECT) { /* data, ptr to that should be */ |
if (pe_add_import(pe, sym_index, |
(sym - |
(Elf32_Sym *) symtab_section->data) | |
0x80000000)) |
|
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; |
} |
} |
error_noabort("undefined symbol '%s'", symbol); |
ret = 1; |
} else |
if (pe->s1->rdynamic |
&& ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { |
|
not_found: |
tcc_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; |
sym->st_other |= ST_PE_EXPORT; |
} |
} |
return ret; |
972,8 → 1305,9 |
|
/*----------------------------------------------------------------------------*/ |
#ifdef PE_PRINT_SECTIONS |
ST void pe_print_section(FILE * f, Section * s) |
{ /* just if you'r curious */ |
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; |
985,21 → 1319,66 |
fprintf(f, "\nlink \"%s\"", s->link->name); |
if (s->reloc) |
fprintf(f, "\nreloc \"%s\"", s->reloc->name); |
fprintf(f, "\nv_addr %08X", s->sh_addr); |
fprintf(f, "\ncontents %08X", l); |
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(Elf32_Sym); |
if (s->sh_type == SHT_REL) |
m = sizeof(Elf32_Rel); |
m = sizeof(ElfW(Sym)); |
else if (s->sh_type == SHT_RELX) |
m = sizeof(ElfW_Rel); |
else |
m = 16; |
|
for (i = 0; i < l;) { |
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) |
1009,36 → 1388,28 |
} |
|
if (s->sh_type == SHT_SYMTAB) { |
Elf32_Sym *sym = (Elf32_Sym *) (p + i); |
ElfW(Sym) *sym = (ElfW(Sym) *) (p + i); |
const char *name = s->link->data + sym->st_name; |
fprintf(f, |
" name:%04X" |
" value:%04X" |
" size:%04X" |
" bind:%02X" |
" type:%02X" |
" other:%02X" |
" shndx:%04X" |
" \"%s\"", |
sym->st_name, |
sym->st_value, |
sym->st_size, |
ELF32_ST_BIND(sym->st_info), |
ELF32_ST_TYPE(sym->st_info), |
sym->st_other, sym->st_shndx, name); |
} else if (s->sh_type == SHT_REL) { |
Elf32_Rel *rel = (Elf32_Rel *) (p + i); |
Elf32_Sym *sym = |
(Elf32_Sym *) s->link->data + ELF32_R_SYM(rel->r_info); |
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, |
" offset:%04X" |
" type:%02X" |
" symbol:%04X" |
" \"%s\"", |
rel->r_offset, |
ELF32_R_TYPE(rel->r_info), |
ELF32_R_SYM(rel->r_info), 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) { |
1055,190 → 1426,454 |
} |
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 |
|
static int pe_test_cmd(const char **pp, const char *cmd) |
/* ------------------------------------------------------------- */ |
/* helper function for load/store to insert one more indirection */ |
|
ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2) |
{ |
const char *p; |
char *q, buf[16]; |
int ret; |
Sym *sym; |
ElfW(Sym) *esym; |
int r2; |
|
p = *pp; |
q = buf; |
while (*p != '\0' && !is_space(*p)) { |
if ((q - buf) < sizeof(buf) - 1) |
*q++ = toup(*p); |
p++; |
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 & ST_PE_IMPORT)) |
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 ((uint32_t)sv->c.i) { |
vpushv(v2); |
vpushi(sv->c.i); |
gen_opi('+'); |
*v2 = *vtop--; |
} |
*q = '\0'; |
ret = !strcmp(buf, cmd); |
*pp = p; |
|
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, addr_t value) |
{ |
return add_elf_sym( |
s1->dynsymtab_section, |
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(int fd, unsigned offset, void *buffer, unsigned len) |
{ |
lseek(fd, offset, SEEK_SET); |
return len == read(fd, buffer, len); |
} |
|
/* ------------------------------------------------------------- |
* This is for compiled windows resources in 'coff' format |
* as generated by 'windres.exe -O coff ...'. |
*/ |
|
static int pe_load_res(TCCState *s1, int fd) |
{ |
struct pe_rsrc_header hdr; |
Section *rsrc_section; |
int i, ret = -1; |
BYTE *ptr; |
unsigned offs; |
|
if (!read_mem(fd, 0, &hdr, sizeof hdr)) |
goto quit; |
|
if (hdr.filehdr.Machine != IMAGE_FILE_MACHINE |
|| 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(fd, 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(fd, offs, &rel, sizeof rel)) |
goto quit; |
// printf("rsrc_reloc: %x %x %x\n", rel.offset, rel.size, rel.type); |
if (rel.type != RSRC_RELTYPE) |
goto quit; |
put_elf_reloc(symtab_section, rsrc_section, |
rel.offset, R_XXX_RELATIVE, 0); |
offs += sizeof rel; |
} |
ret = 0; |
quit: |
return ret; |
} |
|
/* ------------------------------------------------------------- */ |
int pe_load_def_file(TCCState * s1, FILE * fp) |
#ifndef TCC_TARGET_MEOS |
static int pe_load_def(TCCState *s1, int fd) |
{ |
DLLReference *dllref; |
int f = 0, sym_index; |
char *p, line[120], dllname[40]; |
while (fgets(line, sizeof line, fp)) { |
//p = strchr(line, 0); |
while (p > line && p[-1] <= ' ') |
--p; |
*p = 0; |
p = line; |
while (*p && *p <= ' ') |
++p; |
int state = 0, ret = -1, dllindex = 0, ord; |
char line[400], dllname[80], *p, *x; |
FILE *fp; |
|
if (*p && ';' != *p) |
switch (f) { |
fp = fdopen(dup(fd), "rb"); |
while (fgets(line, sizeof line, fp)) |
{ |
p = trimfront(trimback(line, strchr(line, 0))); |
if (0 == *p || ';' == *p) |
continue; |
|
switch (state) { |
case 0: |
if (!pe_test_cmd((const char **)&p, "LIBRARY")) |
return -1; |
while (is_space(*p)) |
p++; |
pstrcpy(dllname, sizeof(dllname), p); |
++f; |
if (0 != strnicmp(p, "LIBRARY", 7)) |
goto quit; |
pstrcpy(dllname, sizeof dllname, trimfront(p+7)); |
++state; |
continue; |
|
case 1: |
if (!pe_test_cmd((const char **)&p, "EXPORTS")) |
return -1; |
++f; |
if (0 != stricmp(p, "EXPORTS")) |
goto quit; |
++state; |
continue; |
|
case 2: |
dllref = |
tcc_malloc(sizeof(DLLReference) + strlen(dllname)); |
strcpy(dllref->name, dllname); |
dllref->level = 0; |
dynarray_add((void ***) &s1->loaded_dlls, |
&s1->nb_loaded_dlls, dllref); |
++f; |
|
dllindex = add_dllref(s1, dllname); |
++state; |
/* fall through */ |
default: |
/* tccpe needs to know from what dll it should import |
the sym */ |
sym_index = add_elf_sym(s1->dynsymtab_section, |
0, 0, ELF32_ST_INFO(STB_GLOBAL, |
STT_FUNC), |
s1->nb_loaded_dlls - 1, |
text_section->sh_num, p); |
/* get ordinal and will store in sym->st_value */ |
ord = 0; |
x = strchr(p, ' '); |
if (x) { |
*x = 0, x = strrchr(x + 1, '@'); |
if (x) { |
char *d; |
ord = (int)strtol(x + 1, &d, 10); |
if (*d) |
ord = 0; |
} |
} |
pe_putimport(s1, dllindex, p, ord); |
continue; |
} |
} |
ret = 0; |
quit: |
fclose(fp); |
return ret; |
} |
#else |
static int pe_load_def(TCCState *s1, int fd) |
{ return 0; } |
#endif |
|
/* ------------------------------------------------------------- */ |
#define TINY_IMPDEF_GET_EXPORT_NAMES_ONLY |
#include "win32/tools/tiny_impdef.c" |
|
static int pe_load_dll(TCCState *s1, const char *dllname, int fd) |
{ |
char *p, *q; |
int index; |
p = get_export_names(fd); |
if (!p) |
return -1; |
index = add_dllref(s1, dllname); |
for (q = p; *q; q += 1 + strlen(q)) |
pe_putimport(s1, index, q, 0); |
tcc_free(p); |
return 0; |
} |
|
/* ------------------------------------------------------------- */ |
void pe_guess_outfile(char *objfilename, int output_type) |
ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd) |
{ |
char *ext = strrchr(objfilename, '.'); |
if (NULL == ext) |
//ext = strchr(objfilename, 0); |
if (output_type == TCC_OUTPUT_DLL) |
strcpy(ext, ".dll"); |
else |
if (output_type == TCC_OUTPUT_EXE) |
strcpy(ext, ".exe"); |
else |
if (output_type == TCC_OUTPUT_OBJ && strcmp(ext, ".o")) |
strcpy(ext, ".o"); |
else |
error("no outputfile given"); |
int ret = -1; |
char buf[10]; |
if (0 == strcmp(tcc_fileextension(filename), ".def")) |
ret = pe_load_def(s1, fd); |
else if (pe_load_res(s1, fd) == 0) |
ret = 0; |
else if (read_mem(fd, 0, buf, sizeof buf) && 0 == strncmp(buf, "MZ", 2)) |
ret = pe_load_dll(s1, tcc_basename(filename), fd); |
return ret; |
} |
|
/* ------------------------------------------------------------- */ |
unsigned long pe_add_runtime(TCCState * s1) |
#ifdef TCC_TARGET_X86_64 |
static unsigned pe_add_uwwind_info(TCCState *s1) |
{ |
if (NULL == s1->uw_pdata) { |
s1->uw_pdata = find_section(tcc_state, ".pdata"); |
s1->uw_pdata->sh_addralign = 4; |
s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0, text_section->sh_num, NULL); |
} |
|
if (0 == s1->uw_offs) { |
/* As our functions all have the same stackframe, we use one entry for all */ |
static const unsigned 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) |
}; |
|
Section *s = text_section; |
unsigned char *p; |
|
section_ptr_add(s, -s->data_offset & 3); /* align */ |
s1->uw_offs = s->data_offset; |
p = section_ptr_add(s, sizeof uw_info); |
memcpy(p, uw_info, sizeof uw_info); |
} |
|
return s1->uw_offs; |
} |
|
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack) |
{ |
TCCState *s1 = tcc_state; |
Section *pd; |
unsigned o, n, d; |
struct /* _RUNTIME_FUNCTION */ { |
DWORD BeginAddress; |
DWORD EndAddress; |
DWORD UnwindData; |
} *p; |
|
d = pe_add_uwwind_info(s1); |
pd = s1->uw_pdata; |
o = pd->data_offset; |
p = section_ptr_add(pd, sizeof *p); |
|
/* record this function */ |
p->BeginAddress = start; |
p->EndAddress = end; |
p->UnwindData = d; |
|
/* put relocations on it */ |
for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress) |
put_elf_reloc(symtab_section, pd, o, R_X86_64_RELATIVE, s1->uw_sym); |
} |
#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(TCCState *s1, struct pe_info *pe) |
{ |
const char *start_symbol; |
unsigned long addr; |
int pe_type = 0; |
|
if (find_elf_sym(symtab_section, "WinMain")) |
if (find_elf_sym(symtab_section, PE_STDSYM("WinMain","@16"))) |
pe_type = PE_GUI; |
else |
if (TCC_OUTPUT_DLL == s1->output_type) |
{ |
if (TCC_OUTPUT_DLL == s1->output_type) { |
pe_type = PE_DLL; |
// need this for 'tccelf.c:relocate_section()' |
/* 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" : NULL |
: PE_DLL == pe_type ? "_dllstart" |
: PE_GUI == pe_type ? "_winstart" : "_start"; |
? 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; |
|
/* grab the startup code from libtcc1 */ |
if (start_symbol) |
if (TCC_OUTPUT_MEMORY != s1->output_type || PE_GUI == pe_type) |
add_elf_sym(symtab_section, |
0, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, |
SHN_UNDEF, start_symbol); |
|
tcc_add_pragma_libs(s1); |
|
if (0 == s1->nostdlib) { |
tcc_add_library(s1, "tcc1"); |
tcc_add_library(s1, "msvcrt"); |
if (PE_DLL == pe_type || PE_GUI == pe_type) { |
tcc_add_library(s1, "kernel32"); |
tcc_add_library(s1, "user32"); |
tcc_add_library(s1, "gdi32"); |
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_err(s1, p) < 0) { |
break; |
} |
} |
} |
|
addr = start_symbol ? |
(unsigned long) tcc_get_symbol_err(s1, start_symbol) : 0; |
if (TCC_OUTPUT_MEMORY == s1->output_type) { |
pe_type = PE_RUN; |
#ifdef TCC_IS_NATIVE |
s1->runtime_main = start_symbol; |
#endif |
} else { |
pe->start_addr = (DWORD)(uintptr_t)tcc_get_symbol_err(s1, start_symbol); |
} |
|
if (s1->output_type == TCC_OUTPUT_MEMORY && addr) { |
/* for -run GUI's, put '_runwinmain' instead of 'main' */ |
add_elf_sym(symtab_section, |
addr, 0, |
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, |
text_section->sh_num, "main"); |
|
/* FreeConsole(); */ |
pe->type = pe_type; |
} |
return addr; |
} |
|
int tcc_output_pe(TCCState * s1, const char *filename) |
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; |
pe.start_addr = pe_add_runtime(s1); |
|
pe_add_runtime(s1, &pe); |
tcc_add_bcheck(s1); |
relocate_common_syms(); /* assign bss adresses */ |
tcc_add_linker_symbols(s1); |
|
ret = pe_check_symbols(&pe); |
if (0 == ret) { |
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 defined(TCC_TARGET_ARM) |
/* we use "console" subsystem by default */ |
pe.subsystem = 9; |
#else |
if (PE_DLL == pe.type || PE_GUI == pe.type) |
pe.subsystem = 2; |
else |
pe.subsystem = 3; |
#endif |
/* Allow override via -Wl,-subsystem=... option */ |
if (s1->pe_subsystem != 0) |
pe.subsystem = s1->pe_subsystem; |
|
/* 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) |
if (s->reloc) { |
relocate_section(s1, s); |
pe_relocate_rva(&pe, s); |
} |
ret = pe_write_pe(&pe); |
} |
if (s1->nb_errors) |
ret = -1; |
else |
ret = pe_write(&pe); |
tcc_free(pe.sec_info); |
} else { |
#ifdef TCC_IS_NATIVE |
pe.thunk = data_section; |
pe_build_imports(&pe); |
#endif |
} |
|
#ifdef PE_PRINT_SECTIONS |
{ |
Section *s; |
FILE *f; |
f = fopen("tccpe.log", "wt"); |
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); |
} |
pe_print_sections(s1, "tcc.log"); |
#endif |
return ret; |
} |
|
/*----------------------------------------------------------------------------*/ |
/* ------------------------------------------------------------- */ |