From 2ab40d8ac365edabfcdac28c9870b7f5a678eff5 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Mon, 18 Nov 2024 23:57:02 +0000 Subject: [PATCH] Create 32-bit PE executables --- elks.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- ld.c | 6 ++ ld.h | 7 +- lib.c | 45 ++++++++-- pe.h | 177 ++++++++++++++++++++++++++++++++++++ 5 files changed, 504 insertions(+), 11 deletions(-) create mode 100644 pe.h diff --git a/elks.c b/elks.c index c15db37..84d56de 100644 --- a/elks.c +++ b/elks.c @@ -6,11 +6,13 @@ #include #include #include +#include #include "aout.h" #include "elks.h" #include "ld.h" #include "lib.h" +#include "pe.h" #include "report.h" #include "write7x.h" @@ -352,8 +354,8 @@ static int relocate (struct elks_object *object, struct elks_relocation_info *r, if (strcmp (symname, "__edata") == 0) { - int32_t data_addr = ((char *) data - (char *) output) - header_size; - data_addr += state->data_size; + int32_t data_addr = ((char *) text - (char *) output) - header_size; + data_addr += state->text_size; write741_to_byte_array (symbol->n_value, data_addr / 16); /*need_relocate = 0;*/ @@ -627,6 +629,261 @@ static int write_elks_object (unsigned long a_entry) { } +static struct msdos_header *doshdr; +static struct pe_header *pehdr; +static struct pe_optional_header *opthdr; + +static struct section_table_entry *section_text; +static struct section_table_entry *section_data; + +unsigned char dos_stub[] = { + + 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, + 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, + + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, + 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, + + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, + 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, + + 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +}; + +static int no_sections = 0; + +static unsigned short chksum (unsigned int checksum, void *base, int count) { + + register int sum = 0; + int *data; + + if (count && base != NULL) { + + data = (int *) base; + + do { + + sum = *(unsigned short *) data + checksum; + data = (int *) ((char *) data + 2); + + checksum = (unsigned short) sum + (sum >> 16); + + } while (--count); + + } + + return checksum + (checksum >> 16); + +} + +static int pe_chksum (void *base, unsigned int size) { + + void *remaining_data; + int remaining_data_size; + + unsigned int pe_header_chksum, file_chksum; + unsigned int pe_header_size; + + pe_header_size = (unsigned long) pehdr - (unsigned long) base + \ + ((unsigned long) &opthdr->Checksum - (unsigned long) pehdr); + + remaining_data_size = (size - pe_header_size - 4) >> 1; + remaining_data = &opthdr->Subsystem; + + pe_header_chksum = chksum (0, base, pe_header_size >> 1); + file_chksum = chksum (pe_header_chksum, remaining_data, remaining_data_size); + + if (size & 1) { + file_chksum += (unsigned short) *((char *) base + size - 1); + } + + return size + file_chksum; + +} + +static int init_pe_object (void) { + + header_size = sizeof (*doshdr) + sizeof (dos_stub) + sizeof (*pehdr) + sizeof (*opthdr); + no_sections = 0; + + if (state->raw_text_size > 0) { + + header_size += sizeof (*section_text); + no_sections++; + + } + + if (state->raw_data_size > 0) { + + header_size += sizeof (*section_data); + no_sections++; + + } + + header_size = ALIGN_UP (header_size, SECTION_ALIGNMENT); + output_size = header_size + ALIGN_UP (state->text_size, SECTION_ALIGNMENT) + ALIGN_UP (state->data_size, SECTION_ALIGNMENT); + + if ((output = malloc (output_size)) == NULL) { + return 2; + } + + memset (output, 0, output_size); + + doshdr = (struct msdos_header *) output; + pehdr = (struct pe_header *) ((char *) doshdr + sizeof (*doshdr) + sizeof (dos_stub)); + opthdr = (struct pe_optional_header *) ((char *) pehdr + sizeof (*pehdr)); + + section_text = (struct section_table_entry *) ((char *) opthdr + sizeof (*opthdr)); + section_data = (struct section_table_entry *) ((char *) section_text + sizeof (*section_text)); + + text = (void *) ((char *) output + header_size); + data = (void *) ((char *) text + ALIGN_UP (state->text_size, SECTION_ALIGNMENT)); + + return 0; + +} + +static int write_pe_object (unsigned long entry) { + + doshdr->e_magic[0] = 'M'; + doshdr->e_magic[1] = 'Z'; + + write721_to_byte_array (doshdr->e_cblp, 0x0090); + write721_to_byte_array (doshdr->e_cp, 0x0003); + + write721_to_byte_array (doshdr->e_cparhdr, ALIGN_UP (sizeof (*doshdr), 16) / 16); + + write721_to_byte_array (doshdr->e_maxalloc, 0xFFFF); + write721_to_byte_array (doshdr->e_sp, 0x00B8); + + write721_to_byte_array (doshdr->e_lfarlc, sizeof (*doshdr)); + write741_to_byte_array (doshdr->e_lfanew, sizeof (*doshdr) + sizeof (dos_stub)); + + memcpy ((char *) output + GET_UINT16 (doshdr->e_lfarlc), dos_stub, sizeof (dos_stub)); + + + pehdr->Signature[0] = 'P'; + pehdr->Signature[1] = 'E'; + + write721_to_byte_array (pehdr->Machine, IMAGE_FILE_MACHINE_I386); + write721_to_byte_array (pehdr->NumberOfSections, no_sections); + write741_to_byte_array (pehdr->TimeDateStamp, time (0)); + write721_to_byte_array (pehdr->SizeOfOptionalHeader, sizeof (*opthdr)); + + { + + unsigned short characteristics = 0; + + characteristics |= IMAGE_FILE_RELOCS_STRIPPED; + characteristics |= IMAGE_FILE_EXECUTABLE_IMAGE; + characteristics |= IMAGE_FILE_LINE_NUMS_STRIPPED; + characteristics |= IMAGE_FILE_LOCAL_SYMS_STRIPPED; + characteristics |= IMAGE_FILE_32BIT_MACHINE; + characteristics |= IMAGE_FILE_DEBUG_STRIPPED; + + write721_to_byte_array (pehdr->Characteristics, characteristics); + + } + + + write721_to_byte_array (opthdr->Magic, IMAGE_FILE_MAGIC_I386); + + opthdr->MajorLinkerVersion = 2; + opthdr->MinorLinkerVersion = 1; + + write741_to_byte_array (opthdr->SizeOfCode, ALIGN_UP (state->text_size, SECTION_ALIGNMENT)); + write741_to_byte_array (opthdr->SizeOfInitializedData, ALIGN_UP (state->data_size, SECTION_ALIGNMENT)); + write741_to_byte_array (opthdr->SizeOfUninitializedData, ALIGN_UP (state->bss_size, SECTION_ALIGNMENT)); + write741_to_byte_array (opthdr->AddressOfEntryPoint, ALIGN_UP ((char *) text - (char *) output, SECTION_ALIGNMENT) + entry); + write741_to_byte_array (opthdr->BaseOfCode, ALIGN_UP ((char *) text - (char *) output, SECTION_ALIGNMENT)); + write741_to_byte_array (opthdr->BaseOfData, ALIGN_UP ((char *) data - (char *) output, SECTION_ALIGNMENT)); + + write741_to_byte_array (opthdr->ImageBase, state->psp); + write741_to_byte_array (opthdr->SectionAlignment, SECTION_ALIGNMENT); + write741_to_byte_array (opthdr->FileAlignment, SECTION_ALIGNMENT); + + write721_to_byte_array (opthdr->MajorOperatingSystemVersion, 4); + write721_to_byte_array (opthdr->MajorImageVersion, 1); + write721_to_byte_array (opthdr->MajorSubsystemVersion, 4); + + write741_to_byte_array (opthdr->SizeOfImage, GET_UINT32 (opthdr->BaseOfData) + ALIGN_UP (GET_UINT32 (opthdr->SizeOfInitializedData), SECTION_ALIGNMENT)); + write741_to_byte_array (opthdr->SizeOfHeaders, ALIGN_UP (header_size, SECTION_ALIGNMENT)); + write741_to_byte_array (opthdr->Checksum, pe_chksum (output, output_size)); + + write721_to_byte_array (opthdr->Subsystem, state->subsystem); + + { + + unsigned long ibss_addr = ((char *) data - (char *) output) + state->data_size; + unsigned long ibss_size = state->bss_size; + + unsigned long stack_addr = ibss_addr + ibss_size; + unsigned long stack_size = ALIGN_UP (stack_addr, SECTION_ALIGNMENT); + + write741_to_byte_array (opthdr->SizeOfStackReserved, SECTION_ALIGNMENT << 9); + write741_to_byte_array (opthdr->SizeOfStackCommit, ALIGN_UP (stack_addr % 16 + stack_size, SECTION_ALIGNMENT)); + write741_to_byte_array (opthdr->SizeOfHeapReserved, SECTION_ALIGNMENT << 8); + write741_to_byte_array (opthdr->SizeOfHeapCommit, ALIGN_UP (stack_addr % 16 + stack_size, SECTION_ALIGNMENT)); + + } + + write741_to_byte_array (opthdr->NumberOfRvaAndSizes, 16); + + + if (state->raw_text_size > 0) { + + unsigned long characteristics = 0; + memcpy (section_text->Name, ".text", 5); + + write741_to_byte_array (section_text->VirtualSize, state->text_size); + write741_to_byte_array (section_text->VirtualAddress, GET_UINT32 (opthdr->BaseOfCode)); + write741_to_byte_array (section_text->SizeOfRawData, state->raw_text_size); + write741_to_byte_array (section_text->PointerToRawData, ((char *) text - (char *) output)); + + characteristics |= IMAGE_SCN_CNT_CODE; + characteristics |= IMAGE_SCN_ALIGN_4BYTES; + characteristics |= IMAGE_SCN_MEM_EXECUTE; + characteristics |= IMAGE_SCN_MEM_READ; + + write741_to_byte_array (section_text->Characteristics, characteristics); + + } + + if (state->raw_data_size > 0) { + + unsigned long characteristics = 0; + memcpy (section_text->Name, ".data", 5); + + write741_to_byte_array (section_data->VirtualSize, state->data_size); + write741_to_byte_array (section_data->VirtualAddress, GET_UINT32 (opthdr->BaseOfData)); + write741_to_byte_array (section_data->SizeOfRawData, state->raw_data_size); + write741_to_byte_array (section_data->PointerToRawData, ((char *) data - (char *) output)); + + characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; + characteristics |= IMAGE_SCN_ALIGN_4BYTES; + characteristics |= IMAGE_SCN_MEM_READ; + characteristics |= IMAGE_SCN_MEM_WRITE; + + write741_to_byte_array (section_data->Characteristics, characteristics); + + } + + + /* write the file */ + if (fwrite ((char *) output, output_size, 1, state->ofp) != 1) { + + report_at (program_name, 0, REPORT_ERROR, "failed to write data to '%s'", state->ofile); + return 1; + + } + + return 0; + +} + + int create_executable_from_elks_objects (void) { struct elks_object *object; @@ -657,6 +914,15 @@ int create_executable_from_elks_objects (void) { } + } else if (state->format == LD_FORMAT_I386_PE) { + + if (init_pe_object ()) { + + report_at (program_name, 0, REPORT_ERROR, "failed to initialize pe object"); + return EXIT_FAILURE; + + } + } for (i = 0; i < state->nb_elks_objs; ++i) { @@ -687,7 +953,7 @@ int create_executable_from_elks_objects (void) { return EXIT_FAILURE; } - if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_I386_AOUT) { + if (state->format == LD_FORMAT_IA16_ELKS || state->format == LD_FORMAT_I386_ELKS || state->format == LD_FORMAT_I386_AOUT || state->format == LD_FORMAT_I386_PE) { entry = get_entry (); } @@ -742,6 +1008,14 @@ int create_executable_from_elks_objects (void) { } + } else if (state->format == LD_FORMAT_I386_PE) { + + if (write_pe_object (entry)) { + + report_at (program_name, 0, REPORT_ERROR, "failed to write pe object"); + return EXIT_FAILURE; + + } } return EXIT_SUCCESS; diff --git a/ld.c b/ld.c index cdb7af5..553a87a 100644 --- a/ld.c +++ b/ld.c @@ -108,6 +108,10 @@ static int process_elks (void *obj, unsigned long sz, const char *fname, int qui }*/ + state->raw_text_size += GET_UINT32 (hdr->a_text); + state->raw_data_size += GET_UINT32 (hdr->a_data); + state->raw_bss_size += GET_UINT32 (hdr->a_bss); + if (state->impure) { state->text_size += GET_UINT32 (hdr->a_text); @@ -667,6 +671,8 @@ int main (int argc, char **argv) { state->impure = 1; + } else if (state->format == LD_FORMAT_I386_PE) { + state->psp = 0x00400000; } for (i = 0; i < state->nb_files; i++) { diff --git a/ld.h b/ld.h index 6f5e289..6fccd1b 100644 --- a/ld.h +++ b/ld.h @@ -24,6 +24,8 @@ struct elks_object { }; +#define GET_UINT16(arr) ((unsigned long) arr[0] | (((unsigned long) arr[1]) << 8)) + #define GET_INT32(arr) ((long) arr[0] | (((long) arr[1]) << 8) | (((long) arr[2]) << 16) | (((long) arr[3]) << 24)) #define GET_UINT32(arr) ((unsigned long) arr[0] | (((unsigned long) arr[1]) << 8) | (((unsigned long) arr[2]) << 16) | (((unsigned long) arr[3]) << 24)) @@ -43,7 +45,10 @@ struct ld_state { struct elks_object **elks_objs; long nb_elks_objs; + unsigned long raw_text_size, raw_data_size, raw_bss_size; unsigned long text_size, data_size, bss_size; + + unsigned long subsystem; char *entry; }; @@ -55,11 +60,11 @@ struct ld_state { #define LD_FORMAT_I386_ELKS 0x04 #define LD_FORMAT_I386_AOUT 0x08 +#define LD_FORMAT_I386_PE 0x10 extern struct ld_state *state; extern const char *program_name; -#define FILE_ALIGNMENT 512 #define SECTION_ALIGNMENT 4096 #define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b)) diff --git a/lib.c b/lib.c index eb87b24..7aeac36 100644 --- a/lib.c +++ b/lib.c @@ -36,19 +36,21 @@ struct ld_option { #define LD_OPTION_MAP 5 #define LD_OPTION_OUTFILE 6 #define LD_OPTION_PSP 7 +#define LD_OPTION_SUBSYSTEM 8 static struct ld_option opts[] = { - { "-N", LD_OPTION_IMPURE, LD_OPTION_NO_ARG }, - { "-T", LD_OPTION_PSP, LD_OPTION_HAS_ARG }, + { "-N", LD_OPTION_IMPURE, LD_OPTION_NO_ARG }, + { "-T", LD_OPTION_PSP, LD_OPTION_HAS_ARG }, - { "-e", LD_OPTION_ENTRY, LD_OPTION_HAS_ARG }, - { "-o", LD_OPTION_OUTFILE, LD_OPTION_HAS_ARG }, + { "-e", LD_OPTION_ENTRY, LD_OPTION_HAS_ARG }, + { "-o", LD_OPTION_OUTFILE, LD_OPTION_HAS_ARG }, - { "--oformat", LD_OPTION_FORMAT, LD_OPTION_HAS_ARG }, - { "--help", LD_OPTION_HELP, LD_OPTION_NO_ARG }, + { "--oformat", LD_OPTION_FORMAT, LD_OPTION_HAS_ARG }, + { "--help", LD_OPTION_HELP, LD_OPTION_NO_ARG }, + { "--subsystem", LD_OPTION_SUBSYSTEM, LD_OPTION_HAS_ARG }, - { 0, 0, 0 } + { 0, 0, 0 } }; @@ -174,6 +176,8 @@ void parse_args (int argc, char **argv, int optind) { } + state->subsystem = 3; + while (optind < argc) { r = argv[optind++]; @@ -276,6 +280,13 @@ void parse_args (int argc, char **argv, int optind) { } + if (xstrcasecmp (optarg, "pe-i386") == 0) { + + state->format = LD_FORMAT_I386_PE; + break; + + } + report_at (program_name, 0, REPORT_ERROR, "unrecognised output format '%s'", optarg); exit (EXIT_FAILURE); @@ -336,6 +347,26 @@ void parse_args (int argc, char **argv, int optind) { } + case LD_OPTION_SUBSYSTEM: { + + long conversion; + char *temp; + + errno = 0; + conversion = strtol (optarg, &temp, 0); + + if (!*optarg || isspace ((int) *optarg) || errno || *temp) { + + report_at (program_name, 0, REPORT_ERROR, "bad number for subsystem"); + exit (EXIT_FAILURE); + + } + + state->subsystem = (unsigned long) conversion; + break; + + } + default: { report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r); diff --git a/pe.h b/pe.h new file mode 100644 index 0000000..59a7724 --- /dev/null +++ b/pe.h @@ -0,0 +1,177 @@ +/****************************************************************************** + * @file pe.h + *****************************************************************************/ +#ifndef _PE_H +#define _PE_H + +struct msdos_header { + + unsigned char e_magic[2]; /* Magic number */ + unsigned char e_cblp[2]; /* Bytes on last page of file */ + unsigned char e_cp[2]; /* Pages in file */ + unsigned char e_crlc[2]; /* Relocations */ + unsigned char e_cparhdr[2]; /* Size of header in paragraphs */ + unsigned char e_minalloc[2]; /* Minimum extra paragraphs needed */ + unsigned char e_maxalloc[2]; /* Maximum extra paragraphs needed */ + unsigned char e_ss[2]; /* Initial (relative) SS value */ + unsigned char e_sp[2]; /* Initial SP value */ + unsigned char e_csum[2]; /* Checksum */ + unsigned char e_ip[2]; /* Initial IP value */ + unsigned char e_cs[2]; /* Initial (relative) CS value */ + unsigned char e_lfarlc[2]; /* File address of relocation table */ + unsigned char e_ovno[2]; /* Overlay number */ + unsigned char e_res[8]; /* Reserved words */ + unsigned char e_oemid[2]; /* OEM identifier (for e_oeminfo) */ + unsigned char e_oeminfo[2]; /* OEM information; e_oemid specific */ + unsigned char e_res2[20]; /* Reserved words */ + unsigned char e_lfanew[4]; /* File address of new exe header */ + +}; + +struct pe_header { + + unsigned char Signature[4]; + + unsigned char Machine[2]; + unsigned char NumberOfSections[2]; + + unsigned char TimeDateStamp[4]; + unsigned char PointerToSymbolTable[4]; + unsigned char NumberOfSymbols[4]; + + unsigned char SizeOfOptionalHeader[2]; + unsigned char Characteristics[2]; + +}; + +#define IMAGE_FILE_MACHINE_I386 0x014C + +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 + +struct pe_optional_header { + + unsigned char Magic[2]; + + unsigned char MajorLinkerVersion; + unsigned char MinorLinkerVersion; + + unsigned char SizeOfCode[4]; + unsigned char SizeOfInitializedData[4]; + unsigned char SizeOfUninitializedData[4]; + + unsigned char AddressOfEntryPoint[4]; + + unsigned char BaseOfCode[4]; + unsigned char BaseOfData[4]; + + + unsigned char ImageBase[4]; + unsigned char SectionAlignment[4]; + unsigned char FileAlignment[4]; + + unsigned char MajorOperatingSystemVersion[2]; + unsigned char MinorOperatingSystemVersion[2]; + unsigned char MajorImageVersion[2]; + unsigned char MinorImageVersion[2]; + unsigned char MajorSubsystemVersion[2]; + unsigned char MinorSubsystemVersion[2]; + + unsigned char Win32VersionValue[4]; + unsigned char SizeOfImage[4]; + unsigned char SizeOfHeaders[4]; + unsigned char Checksum[4]; + + unsigned char Subsystem[2]; + unsigned char DllCharacteristics[2]; + + unsigned char SizeOfStackReserved[4]; + unsigned char SizeOfStackCommit[4]; + unsigned char SizeOfHeapReserved[4]; + unsigned char SizeOfHeapCommit[4]; + + unsigned char LoaderFlags[4]; + unsigned char NumberOfRvaAndSizes[4]; + + + /*unsigned char Reserved[128];*/ + unsigned char Reserved[32 * 4]; + +}; + +#define IMAGE_FILE_MAGIC_I386 0x010B + +struct section_table_entry { + + char Name[8]; + + unsigned char VirtualSize[4]; + unsigned char VirtualAddress[4]; + + unsigned char SizeOfRawData[4]; + unsigned char PointerToRawData[4]; + unsigned char PointerToRelocations[4]; + unsigned char PointerToLinenumbers[4]; + + unsigned char NumberOfRelocations[2]; + unsigned char NumberOfLinenumbers[2]; + + unsigned char Characteristics[4]; + +}; + +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +struct relocation_entry { + + unsigned int VirtualAddress; + unsigned int SymbolTableIndex; + + unsigned short Type; + +}; + +#define RELOCATION_ENTRY_SIZE 10 + +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_REL32 0x0014 + +struct symbol_table_entry { + + char Name[8]; + unsigned int Value; + + signed short SectionNumber; + unsigned short Type; + + unsigned char StorageClass; + unsigned char NumberOfAuxSymbols; + +}; + +#define SYMBOL_TABLE_ENTRY_SIZE 18 +#define IMAGE_SYM_UNDEFINED 0 + +#define IMAGE_SYM_ABSOLUTE -1 +#define IMAGE_SYM_DEBUG -2 + +#define IMAGE_SYM_TYPE_NULL 0 +#define IMAGE_SYM_DTYPE_NULL 0 + +#define IMAGE_SYM_CLASS_EXTERNAL 2 +#define IMAGE_SYM_CLASS_STATIC 3 +#define IMAGE_SYM_CLASS_FILE 103 + +#endif /* _PE_H */ -- 2.34.1