// #include #include #define NULL 0U typedef long HRESULT; #define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) #define VOID void typedef unsigned __int64 ULONGLONG; typedef __int64 LONGLONG; typedef char CHAR; typedef short SHORT; typedef long LONG; typedef unsigned long DWORD; typedef int BOOL; typedef unsigned char BYTE; typedef unsigned short WORD; typedef int INT; typedef unsigned int UINT; typedef unsigned int *PUINT; typedef void* HANDLE; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; typedef signed char s8; typedef signed short s16; typedef signed int s32; typedef signed long long s64; typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; } DUMMYSTRUCTNAME; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER; typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME, *PFILETIME, *LPFILETIME; #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define IMAGE_NT_SIGNATURE 0x00004550 // PE00 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b 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 LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; // // Directory format. // typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 typedef struct _IMAGE_OPTIONAL_HEADER64 { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; ULONGLONG 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; ULONGLONG SizeOfStackReserve; ULONGLONG SizeOfStackCommit; ULONGLONG SizeOfHeapReserve; ULONGLONG SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; #define IMAGE_SIZEOF_SECTION_HEADER 40 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_SHORT_NAME 8 #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file. #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references). #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file. #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file. #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Aggressively trim working set #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed. #define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine. #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media, copy and run from the swap file. #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net, copy and run from the swap file. #define IMAGE_FILE_SYSTEM 0x1000 // System File. #define IMAGE_FILE_DLL 0x2000 // File is a DLL. #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed. #define IMAGE_FILE_MACHINE_UNKNOWN 0 #define IMAGE_FILE_MACHINE_TARGET_HOST 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest. #define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. #define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian #define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian #define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian #define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2 #define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP #define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian #define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 #define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian #define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian #define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5 #define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian #define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian #define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian #define IMAGE_FILE_MACHINE_AM33 0x01d3 #define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian #define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 #define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64 #define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS #define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64 #define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS #define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS #define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 #define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon #define IMAGE_FILE_MACHINE_CEF 0x0CEF #define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code #define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) #define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian #define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian #define IMAGE_FILE_MACHINE_CEE 0xC0EE 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_FILE_HEADER 20 #define MEM_COMMIT 0x00001000 #define MEM_RESERVE 0x00002000 #define MEM_DECOMMIT 0x00004000 #define MEM_RELEASE 0x00008000 #define MEM_FREE 0x00010000 #define PAGE_NOACCESS 0x01 #define PAGE_READONLY 0x02 #define PAGE_READWRITE 0x04 #define PAGE_WRITECOPY 0x08 #define PAGE_EXECUTE 0x10 #define PAGE_EXECUTE_READ 0x20 #define PAGE_EXECUTE_READWRITE 0x40 #define PAGE_EXECUTE_WRITECOPY 0x80 #define PAGE_GUARD 0x100 #define PAGE_NOCACHE 0x200 #define PAGE_WRITECOMBINE 0x400 #define FILE_SHARE_READ 0x00000001 #define FILE_SHARE_WRITE 0x00000002 #define FILE_SHARE_DELETE 0x00000004 #define GENERIC_READ (0x80000000L) #define GENERIC_WRITE (0x40000000L) #define GENERIC_EXECUTE (0x20000000L) #define GENERIC_ALL (0x10000000L) #define STD_INPUT_HANDLE ((DWORD)-10) #define STD_OUTPUT_HANDLE ((DWORD)-11) #define STD_ERROR_HANDLE ((DWORD)-12) // // Constants // #define CREATE_NEW 1 #define CREATE_ALWAYS 2 #define OPEN_EXISTING 3 #define OPEN_ALWAYS 4 #define TRUNCATE_EXISTING 5 typedef __int64 LONG_PTR, *PLONG_PTR; #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) #define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF) #define INVALID_SET_FILE_POINTER ((DWORD)-1) #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem. void* GetStdHandle(u32); s32 GetFileSizeEx(void*, LARGE_INTEGER*); void* CreateFileA(const char*, u32, u32, void*, u32, u32, void*); s32 ReadFile(void*, void*, u32, u32*, void*); s32 WriteFile(void*, void*, u32, u32*, void*); s32 CloseHandle(void*); u32 GetEnvironmentVariableA(const char*, char*, u32); void GetSystemTimeAsFileTime(PFILETIME); char* GetCommandLineA(); void* VirtualAlloc(void*, u64, u32, u32); s32 VirtualFree(void*, u64, u32); _Static_assert(sizeof(s8) == 1U, ""); _Static_assert(sizeof(s16) == 2U, ""); _Static_assert(sizeof(s32) == 4U, ""); _Static_assert(sizeof(s64) == 8U, ""); _Static_assert(sizeof(u8) == 1U, ""); _Static_assert(sizeof(u16) == 2U, ""); _Static_assert(sizeof(u32) == 4U, ""); _Static_assert(sizeof(u64) == 8U, ""); #define ALIGN(x, align) (((x) + (align - 1U)) / (align)) * (align) #define ASSERT(x) if (!(x)) { __debugbreak(); } static u8 is_alpha(char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); } static u8 is_decimal_digit(char c) { return '0' <= c && c <= '9'; } // TODO: Improve basic functions perfs // max_size: str allocated size static size_t str_len(const char* str, size_t max_size) { const char* c = str; size_t count = 0U; while (*c != '\0' && count < max_size) { c++; count++; } return count; } static void str_cat(char* out, const char* str, size_t c_str) { char* cur_out = out; const char* cur_str = str; u32 index = 0U; while(index < c_str) { *cur_out = *cur_str; cur_out++; cur_str++; index++; } } static void mem_copy(void* src, void* dst, size_t size) { u8* byte_src = (u8*)src; u8* byte_dst = (u8*)dst; for (u32 index = 0U; index < size; index++) { byte_dst[index] = byte_src[index]; } } // static void mem_set(void* ptr, size_t size, u8 value) { static void* memset(void* ptr, size_t size, u8 value) { u8* bytes = ptr; for (u32 index = 0U; index < size; index++) { bytes[index] = value; } return ptr; } static void u64toa(u64 number, char* str, u32 base) { static char codes[16U] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', }; u32 remainder = 0U; u32 size = 0U; if (number == 0) { str[0U] = codes[0U]; return; } for(; number != 0U; size++) { remainder = number % base; number /= base; str[size] = codes[remainder]; } for(u32 i = 0; i < size / 2U; i++) { char c = str[i]; str[i] = str[size - i - 1]; str[size - i - 1] = c; } } static u64 atou64(const char* str, u32 len) { u64 number = 0U; u32 base = 10U; const char* c = str; if (str[0] == '0' && str[1] == 'x') { base = 16U; c += 2U; } for (; (u32)(c - str) < len; c++) { number *= base; u8 offset = 0U; if ('0' <= *c && *c <= '9') { offset = 48U; } else if ('a' <= *c && *c <= 'f') { offset = 87U; } else if ('A' <= *c && *c <= 'F') { offset = 55U; } else { return 0; } ASSERT(0 <= *c) number += ((u8)*c - offset); } return number; } static void print(const char* format, ...) { va_list args; va_start(args, format); char formatted_str[4096U] = { 0 }; char* cur_char = formatted_str; const char* cur_fmt = format; while(*cur_fmt != '\0') { if(*cur_fmt == '%') { cur_fmt++; if (*cur_fmt == 's') { const char* str = va_arg(args, const char*); size_t length = 0U; if (cur_fmt[1U] == '.') { const char* number_start = &cur_fmt[2U]; u32 size = 0U; while(is_decimal_digit(*(number_start + size))) { size++; } if (size > 0U) { length = (size_t)atou64(number_start, size); } cur_fmt += size + 1U; } if (length == 0U && str != NULL) { length = str_len(str, sizeof(formatted_str)); str_cat(cur_char, str, length); cur_char += length; } } else if(*cur_fmt == 'c') { *cur_char = (char)va_arg(args, int); cur_char += 1U; } else if (*cur_fmt == 'x' && is_alpha(cur_fmt[1])) { u32 base = 16U; *cur_char = '0'; cur_char++; *cur_char = 'x'; cur_char++; cur_fmt++; if(*cur_fmt == 'b') { u8 number = (u8)va_arg(args, int); u64toa((u64)number, cur_char, base); size_t length = 0U; length = str_len(formatted_str, sizeof(formatted_str)); cur_char = formatted_str + length; } else if(*cur_fmt == 'u') { u64 number = va_arg(args, u64); u64toa(number, cur_char, base); size_t length = 0U; length = str_len(formatted_str, sizeof(formatted_str)); cur_char = formatted_str + length; } } else if(*cur_fmt == 'b') { u8 number = (u8)va_arg(args, int); u64toa((u64)number, cur_char, 10U); size_t length = 0U; length = str_len(formatted_str, sizeof(formatted_str)); cur_char = formatted_str + length; } else if(*cur_fmt == 'u') { u64 number = va_arg(args, u64); u64toa(number, cur_char, 10U); size_t length = 0U; length = str_len(formatted_str, sizeof(formatted_str)); cur_char = formatted_str + length; } } else { *cur_char = *cur_fmt; cur_char++; } cur_fmt++; } va_end(args); HANDLE standard_err = GetStdHandle(STD_ERROR_HANDLE); ASSERT(((u8*)cur_char - (u8*)formatted_str) < 0xffffffff) DWORD length = (DWORD)((u8*)cur_char - (u8*)formatted_str); SUCCEEDED(WriteFile(standard_err, formatted_str, length, NULL, NULL)); } // // Section characteristics. // // IMAGE_SCN_TYPE_REG 0x00000000 // Reserved. // IMAGE_SCN_TYPE_DSECT 0x00000001 // Reserved. // IMAGE_SCN_TYPE_NOLOAD 0x00000002 // Reserved. // IMAGE_SCN_TYPE_GROUP 0x00000004 // Reserved. #define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved. // IMAGE_SCN_TYPE_COPY 0x00000010 // Reserved. #define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code. #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data. #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data. #define IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved. #define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information. #define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable. #define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable. #define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable. #define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable. static const u32 BSS_SECTION_FLAGS = (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE); // static const u32 DRECTVE_SECTION_FLAGS = IMAGE_SCN_LNK_INFO; static const u32 DATA_SECTION_FLAGS = (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE); static const u32 TEXT_SECTION_FLAGS = (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ); // static const u64 lib_signature = 0x0A3E686372613C21; // IMAGE_ARCHIVE_START enum { NONE_SECTION_FLAG = 0U, TEXT_SECTION_FLAG = 1U, DATA_SECTION_FLAG = 2U, BSS_SECTION_FLAG = 4U, }; // we allow for only one of each type struct COFF_sections { u8* file_buffer; u32 text_section_off; u32 text_section_size; u32 data_section_off; u32 data_section_size; u32 bss_section_off; u32 bss_section_size; u32 symbol_table_off; u32 symbols_count; u32 string_table_off; u32 string_section_size; }; static struct COFF_sections* read_COFFs_header(const char* obj_files[], u32 obj_files_count, u8* sections_flag, u32* c_COFF_sections) { ASSERT(*sections_flag == 0U); if (obj_files_count == 0U) { return NULL; } // TODO: Make an array type u32 nb_sections_size = sizeof(struct COFF_sections) * obj_files_count; struct COFF_sections* sections = (struct COFF_sections*)VirtualAlloc(NULL, nb_sections_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); memset((void*)sections, nb_sections_size, 0U); for (u32 obj_id = 0U; obj_id < obj_files_count; obj_id++) { struct COFF_sections* section = §ions[obj_id]; const char* obj_file = obj_files[obj_id]; HANDLE obj_file_handle = CreateFileA(obj_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0U, NULL); if (!obj_file_handle && obj_file_handle != INVALID_HANDLE_VALUE) { continue; } LARGE_INTEGER file_size = { 0 }; GetFileSizeEx(obj_file_handle, &file_size); section->file_buffer = (u8*)VirtualAlloc(NULL, file_size.DUMMYSTRUCTNAME.LowPart, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); IMAGE_FILE_HEADER* header = (IMAGE_FILE_HEADER*)section->file_buffer; IMAGE_SECTION_HEADER* section_table = (IMAGE_SECTION_HEADER*)(((u8*)header) + sizeof(*header)); u32 bread = 0U; if (!ReadFile(obj_file_handle, (void*)header, file_size.DUMMYSTRUCTNAME.LowPart, &bread, NULL)) { CloseHandle(obj_file_handle); continue; } section->symbol_table_off = header->PointerToSymbolTable; section->symbols_count = header->NumberOfSymbols; for (u32 section_index = 0U; section_index < header->NumberOfSections; section_index++) { IMAGE_SECTION_HEADER* curr_section = §ion_table[section_index]; if ((curr_section->Characteristics & BSS_SECTION_FLAGS) == BSS_SECTION_FLAGS) { if (section->bss_section_off != 0U) { print("BSS section already found"); __debugbreak(); } *sections_flag |= BSS_SECTION_FLAG; section->bss_section_off = curr_section->PointerToRawData; section->bss_section_size = curr_section->SizeOfRawData; } else if ((curr_section->Characteristics & DATA_SECTION_FLAGS) == DATA_SECTION_FLAGS) { if (section->data_section_size != 0U) { print("data section already found"); __debugbreak(); } *sections_flag |= DATA_SECTION_FLAG; section->data_section_off = curr_section->PointerToRawData; section->data_section_size = curr_section->SizeOfRawData; } else if ((curr_section->Characteristics & TEXT_SECTION_FLAGS) == TEXT_SECTION_FLAGS) { if (section->text_section_off != 0U) { print("text section already found"); __debugbreak(); } *sections_flag |= TEXT_SECTION_FLAG; section->text_section_off = curr_section->PointerToRawData; section->text_section_size = curr_section->SizeOfRawData; } } CloseHandle(obj_file_handle); } *c_COFF_sections = obj_files_count; return sections; } // static void read_COFF(u8* buffer) { // u8* next_byte = buffer; // IMAGE_FILE_HEADER header = *(IMAGE_FILE_HEADER*)next_byte; // next_byte += sizeof(header); // // if (header.Machine == IMAGE_FILE_MACHINE_AMD64) { // print("MACHINE: x64\n"); // } // // print("SECTIONS: %u\n", header.NumberOfSections); // print("SYMBOLS COUNT: %u\n", header.NumberOfSymbols); // // if (header.SizeOfOptionalHeader > 0U) { // IMAGE_OPTIONAL_HEADER64 optional_header = *(IMAGE_OPTIONAL_HEADER64*)next_byte; // print("MAGIC: %u\n", optional_header.Magic); // next_byte += header.SizeOfOptionalHeader; // } // // IMAGE_SYMBOL* symbol_table = (IMAGE_SYMBOL*)(buffer + header.PointerToSymbolTable); // u8* string_table = (u8*)symbol_table + sizeof(*symbol_table) * header.NumberOfSymbols; // // IMAGE_SECTION_HEADER* sections = (IMAGE_SECTION_HEADER*)next_byte; // next_byte += sizeof(sections[0]) * header.NumberOfSections; // for (u32 section_index = 0U; section_index < header.NumberOfSections; section_index++) { // IMAGE_SECTION_HEADER* section = §ions[section_index]; // print("\nSECTION NAME: %s\n", section->Name); // print("SECTION OFFSET: %u\n", section->PointerToRawData); // print("SECTION SIZE: %u\n", section->SizeOfRawData); // print("SECTION RELOCATIONS COUNT: %u\n", section->NumberOfRelocations); // // u8* reloc_start = &buffer[section->PointerToRelocations]; // IMAGE_RELOCATION* cur_reloc_entry = (IMAGE_RELOCATION*)reloc_start; // // // Section relocations // for (u32 reloc_index = 0U; reloc_index < section->NumberOfRelocations; reloc_index++, cur_reloc_entry++) { // print("SYMBOL INDEX : %u\n", cur_reloc_entry->SymbolTableIndex); // // IMAGE_SYMBOL* sym = symbol_table + cur_reloc_entry->SymbolTableIndex; // if (sym->N.Name.Short == 0U) { // use longname // print("SYMBOL NAME : %s\n", string_table + sym->N.Name.Long); // } else { // print("SYMBOL NAME : %s.8\n", sym->N.ShortName); // } // // if (cur_reloc_entry->Type == IMAGE_REL_AMD64_ABSOLUTE) { // print("IMAGE_REL_AMD64_ABSOLUTE\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_ADDR64) { // print("IMAGE_REL_AMD64_ADDR64\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_ADDR32) { // print("IMAGE_REL_AMD64_ADDR32\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_ADDR32NB) { // print("IMAGE_REL_AMD64_ADDR32NB\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_REL32) { // print("IMAGE_REL_AMD64_REL32\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_REL32_1) { // print("IMAGE_REL_AMD64_REL32_1\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_REL32_2) { // print("IMAGE_REL_AMD64_REL32_2\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_REL32_3) { // print("IMAGE_REL_AMD64_REL32_3\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_REL32_4) { // print("IMAGE_REL_AMD64_REL32_4\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_REL32_5) { // print("IMAGE_REL_AMD64_REL32_5\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_SECTION) { // print("IMAGE_REL_AMD64_SECTION\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_SECREL) { // print("IMAGE_REL_AMD64_SECREL\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_SECREL7) { // print("IMAGE_REL_AMD64_SECREL7\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_TOKEN) { // print("IMAGE_REL_AMD64_TOKEN\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_SREL32) { // print("IMAGE_REL_AMD64_SREL32\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_PAIR) { // print("IMAGE_REL_AMD64_PAIR\n"); // } else if (cur_reloc_entry->Type == IMAGE_REL_AMD64_SSPAN32) { // print("IMAGE_REL_AMD64_SSPAN32\n"); // } // } // // if (section->Characteristics & DRECTVE_SECTION_FLAGS) // .drectve // { // u8* drectve_start = &buffer[section->PointerToRawData]; // print("%s\n", drectve_start); // } else if ((section->Characteristics & DATA_SECTION_FLAGS) == DATA_SECTION_FLAGS) { // .data // u8* data_start = &buffer[section->PointerToRawData]; // u8* cur_data = data_start; // while ((cur_data - data_start) < section->SizeOfRawData) { // size_t len = str_len((const char*) cur_data, section->SizeOfRawData); // if (len == 0U) { // cur_data++; // } else { // print("%s\n", cur_data); // cur_data += len; // } // } // } else if ((section->Characteristics & TEXT_SECTION_FLAGS) == TEXT_SECTION_FLAGS) { // .text // // u8* text_start = &buffer[section->pointer_to_raw_data]; // } // } // print("\n"); // // // TODO: Check that this doesn't overlap with sections relocation symbols // IMAGE_SYMBOL* cur_symbol = symbol_table; // for (u32 symbol_index = 0U; symbol_index < header.NumberOfSymbols; symbol_index++, cur_symbol++) { // // if (cur_symbol->N.Name.Short == 0U) { // // use longname // print("SYMBOL NAME : %s\n", string_table + cur_symbol->N.Name.Long); // } else { // print("SYMBOL NAME : %s.8\n", cur_symbol->N.ShortName); // } // // if (cur_symbol->SectionNumber > 1) { // print("SECTION: %s\n", sections[(u16)cur_symbol->SectionNumber - 1U].Name); // } else if (cur_symbol->SectionNumber == IMAGE_SYM_UNDEFINED) { // print("SECTION: EXTERNAL SYMBOL\n"); // } else if (cur_symbol->SectionNumber == IMAGE_SYM_ABSOLUTE) { // print("SECTION: ABSOLUTE\n"); // } else if (cur_symbol->SectionNumber == IMAGE_SYM_DEBUG) { // print("SECTION: DEBUG\n"); // } // // if (cur_symbol->NumberOfAuxSymbols > 0U) { // cur_symbol += cur_symbol->NumberOfAuxSymbols; // symbol_index++; // } // } // print("\n"); // } #define BSWAP32(x) ((x & 0x000000FF) << 24U) | ((x & 0x0000FF00) << 8U) | ((x & 0x00FF0000) >> 8U) | ((x & 0xFF000000) >> 24U) // static void read_lib(u8* buffer) { // u8* next_byte = buffer; // // { // print("FIRST LINKER MEMBER\n"); // IMAGE_ARCHIVE_MEMBER_HEADER* first_linker_member = (IMAGE_ARCHIVE_MEMBER_HEADER*)next_byte; // next_byte += sizeof(*first_linker_member); // if (first_linker_member->Name[0U] != '/') { // IMAGE_ARCHIVE_LINKER_MEMBER // return; // } // // u32 symbols_count = BSWAP32(*(u32*)next_byte); // next_byte += sizeof(symbols_count); // // u32* first_linker_offsets = (u32*)next_byte; // next_byte += sizeof(*first_linker_offsets) * symbols_count; // // u8* first_linker_string_table = next_byte; // u8* cur_string = first_linker_string_table; // for (u32 index = 0U; index < symbols_count; index++) { // size_t size = str_len((const char*)cur_string, 256U); // print("%s\n", cur_string); // cur_string += size + 1U; // } // // next_byte += cur_string - first_linker_string_table; // } // // u32 archive_members_count = 0U; // { // print("SECOND LINKER MEMBER\n"); // IMAGE_ARCHIVE_MEMBER_HEADER* second_linker_member = (IMAGE_ARCHIVE_MEMBER_HEADER*)next_byte; // next_byte += sizeof(*second_linker_member); // if (second_linker_member->Name[0U] != '/') { // IMAGE_ARCHIVE_LINKER_MEMBER // return; // } // // archive_members_count = *(u32*)next_byte; // next_byte += sizeof(archive_members_count); // // u32* archive_members_offsets = (u32*)next_byte; // next_byte += sizeof(*archive_members_offsets) * archive_members_count; // // u32 symbols_count = *(u32*)next_byte; // next_byte += sizeof(symbols_count); // // u16* symbols_indices = (u16*)next_byte; // next_byte += sizeof(*symbols_indices) * symbols_count; // // u8* second_linker_string_table = next_byte; // u8* cur_string = second_linker_string_table; // for (u32 index = 0U; index < symbols_count; index++) { // size_t size = str_len((const char*)cur_string, 256U); // print("%s\n", cur_string); // cur_string += size + 1U; // } // next_byte += cur_string - second_linker_string_table; // } // // { // IMAGE_ARCHIVE_MEMBER_HEADER* longnames_member = (IMAGE_ARCHIVE_MEMBER_HEADER*)next_byte; // if (longnames_member->Name[0U] == '/' && longnames_member->Name[1U] == '/') { // IMAGE_ARCHIVE_LONGNAMES_MEMBER // next_byte += sizeof(*longnames_member); // } // } // // print("ARCHIVE COUNT: %u\n", archive_members_count); // for (u32 archive_index = 0U; archive_index < archive_members_count; archive_index++) { // next_byte += (u64)next_byte % 2U; // IMAGE_ARCHIVE_MEMBER_HEADER* header = (IMAGE_ARCHIVE_MEMBER_HEADER*)next_byte; // next_byte += sizeof(*header); // // u32 size = 0U; // for (u8* c = (u8*)header->Size; *c != 0x20 && size< sizeof(header->Size) / sizeof(header->Size[0U]); c++, size++) { // } // u64 archive_size = atou64((char*)header->Size, size); // // // print("ARCHIVE NAME: %s.16\n", header->Name); // print("ARCHIVE SIZE: %s.10, %u\n", header->Size, archive_size); // // if (header->Name[0U] == '/') { // Special member // next_byte += archive_size; // continue; // } // // if (*(u16*)next_byte == 0U) { // IMPORT_OBJECT_HEADER* import_header = (IMPORT_OBJECT_HEADER*)next_byte; // char* import_name = (char*)next_byte + sizeof(*import_header); // // size_t len = str_len((char*)import_name, 256U); // print("IMPORT NAME: %s\n", import_name); // // char* dll_name = import_name + len; // print("DLL NAME: %s\n", dll_name); // } else { // read_COFF(next_byte); // } // next_byte += archive_size; // print("\n"); // } // } struct PE_headers { IMAGE_DOS_HEADER dos_header; u8 dos_stub[88U]; u32 nt_signature; IMAGE_FILE_HEADER img_header; IMAGE_OPTIONAL_HEADER64 opt_header; }; #define SECTION_ALIGNMENT 4096U // default values for optional header #define FILE_ALIGNMENT 512U // default values for optional header #define OBJ_EXT 0x6A626F2E // .obj static void write_exe_headers(struct PE_headers * buffer, u16 c_sections, u32 addr_entry_point, u32 base_of_code, u32 size_of_code, u32 size_of_headers, u32 size_of_image) { static struct PE_headers PE_headers = { .dos_header = { .e_magic = IMAGE_DOS_SIGNATURE, .e_cblp = 0x0U, .e_cp = 0x0U, .e_crlc = 0x0U, .e_cparhdr = 0x0U, .e_minalloc = 0x0U, .e_maxalloc = 0x0U, .e_ss = 0x0U, .e_sp = 0x0U, .e_csum = 0x0U, .e_ip = 0x0U, .e_cs = 0x0U, .e_lfarlc = 0x0U, .e_ovno = 0x0U, .e_res = { 0x0 }, .e_oemid = 0x0U, .e_oeminfo = 0x0U, .e_res2 = { 0x0 }, .e_lfanew = __builtin_offsetof(struct PE_headers, nt_signature), }, .dos_stub = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .nt_signature = IMAGE_NT_SIGNATURE, .img_header = { .Machine = IMAGE_FILE_MACHINE_AMD64, .NumberOfSections = 0U, .TimeDateStamp = 0U, .PointerToSymbolTable = 0U, .NumberOfSymbols = 0U, .SizeOfOptionalHeader = sizeof(PE_headers.opt_header), .Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE, }, .opt_header = { .Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC, .MajorLinkerVersion = 0U, .MinorLinkerVersion = 1U, .SizeOfCode = 0U, .SizeOfInitializedData = 0U, .SizeOfUninitializedData = 0U, .AddressOfEntryPoint = 0U, .BaseOfCode = 0U, .ImageBase = 0x00400000U, // default value for Windows NT/2000/XP/95/98/ME .SectionAlignment = SECTION_ALIGNMENT, .FileAlignment = FILE_ALIGNMENT, .MajorOperatingSystemVersion = 6U, .MinorOperatingSystemVersion = 0U, .MajorImageVersion = 0U, .MinorImageVersion = 0U, .MajorSubsystemVersion = 6U, .MinorSubsystemVersion = 0U, .Win32VersionValue = 0U, .SizeOfImage = 0U, // must be aligned on section alignment .SizeOfHeaders = 0U, // must be aligned on file alignment .CheckSum = 0U, .Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI, .DllCharacteristics = 0U, .SizeOfStackReserve = 4096U * 10U, // Arbitrary values .SizeOfStackCommit = 4096U * 10U, // Arbitrary values .SizeOfHeapReserve = 4096U * 10U, // Arbitrary values .SizeOfHeapCommit = 4096U * 10U, // Arbitrary values .LoaderFlags = 0U, .NumberOfRvaAndSizes = 0U, .DataDirectory = { 0x0 }, }, }; FILETIME filetime = { 0U }; GetSystemTimeAsFileTime(&filetime); PE_headers.img_header.TimeDateStamp = filetime.dwLowDateTime; PE_headers.img_header.NumberOfSections = c_sections; PE_headers.opt_header.AddressOfEntryPoint = addr_entry_point; PE_headers.opt_header.BaseOfCode = base_of_code; PE_headers.opt_header.SizeOfCode = size_of_code; PE_headers.opt_header.SizeOfImage = size_of_image; PE_headers.opt_header.SizeOfHeaders = size_of_headers; mem_copy(&PE_headers, buffer, sizeof(PE_headers)); } static void write_text_section_header(IMAGE_SECTION_HEADER* section_table, u16* section_count) { IMAGE_SECTION_HEADER* text_section_header = §ion_table[*section_count]; mem_copy(".text", text_section_header->Name, 6U); text_section_header->VirtualAddress = 0x0U; text_section_header->PointerToRawData = 0x0U; text_section_header->PointerToRelocations = 0x0U; text_section_header->PointerToLinenumbers = 0x0U; text_section_header->NumberOfRelocations = 0U; text_section_header->NumberOfLinenumbers = 0U; text_section_header->Characteristics = TEXT_SECTION_FLAGS; (*section_count)++; } static void write_data_section_header(IMAGE_SECTION_HEADER* section_table, u16* section_count) { IMAGE_SECTION_HEADER* data_section_header = §ion_table[*section_count]; mem_copy(".text", data_section_header->Name, 6U); data_section_header->VirtualAddress = 0x0U; data_section_header->PointerToRawData = 0x0U; data_section_header->PointerToRelocations = 0x0U; data_section_header->PointerToLinenumbers = 0x0U; data_section_header->NumberOfRelocations = 0U; data_section_header->NumberOfLinenumbers = 0U; data_section_header->Characteristics = DATA_SECTION_FLAGS; (*section_count)++; } static void write_bss_section_header(IMAGE_SECTION_HEADER* section_table, u16* section_count) { IMAGE_SECTION_HEADER* bss_section_header = §ion_table[*section_count]; mem_copy(".text", bss_section_header->Name, 6U); bss_section_header->VirtualAddress = 0x0U; bss_section_header->PointerToRawData = 0x0U; bss_section_header->PointerToRelocations = 0x0U; bss_section_header->PointerToLinenumbers = 0x0U; bss_section_header->NumberOfRelocations = 0U; bss_section_header->NumberOfLinenumbers = 0U; bss_section_header->Characteristics = BSS_SECTION_FLAGS; (*section_count)++; } int mainCRTStartup(void) { // copy of the command line passed to the program char* args_string = VirtualAlloc(NULL, 4096U, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // arg array const char** argv = VirtualAlloc(NULL, 4096U, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // arg size array u32* argc = VirtualAlloc(NULL, 4096U, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); argv[0U] = args_string; argc[0U] = 0U; u32 c_arg = 1U; { char* args = GetCommandLineA(); for (u32 char_index = 0U; *(args + char_index) != '\0'; char_index++) { char c = *(args + char_index); argc[c_arg - 1U]++; if (c == ' ') { args_string[char_index] = '\0'; argv[c_arg] = &args_string[char_index + 1U]; c_arg++; argc[c_arg - 1U] = 0U; } else { args_string[char_index] = c; } } } for (u32 arg_index = 0U; arg_index < c_arg; arg_index++) { const char* arg = argv[arg_index]; u32 arg_size = argc[arg_index]; print("arg : %s, arg size : %u\n", arg, arg_size); } // const char* obj_files[64U] = { NULL }; const char* obj_files[64U]; u32 c_obj_files = 0U; for (u32 arg_index = 0U; arg_index < c_arg; arg_index++) { const char* arg = argv[arg_index]; u32 arg_size = argc[arg_index]; u32 arg_ext = *(u32*)(arg + arg_size - 5U); if (arg_ext == OBJ_EXT) { obj_files[c_obj_files] = arg; c_obj_files++; } } char* lib_paths[64U] = { 0U }; u32 c_lib_paths = 0U; char* lib_path_env = VirtualAlloc(NULL, 4096U, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); { DWORD ret = GetEnvironmentVariableA("LIB", (char*)lib_path_env, 4096U); if (ret != 0U) { print ("LIB : %s\n", lib_path_env); lib_paths[0U] = lib_path_env; for (char* c = lib_path_env; *c != '\0'; c++) { if (*c == ';') { c_lib_paths++; *c = '\0'; lib_paths[c_lib_paths] = c + 1U; } } if (lib_paths[c_lib_paths][0U] != '\0') { c_lib_paths++; } } } for (u32 index = 0U; index < c_lib_paths; index++) { print("LIB %u: %s\n", index, lib_paths[index]); } // char* filepath = VirtualAlloc(NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // for (u32 arg_index = 1U; arg_index < c_arg; arg_index++) { // // NtCreateFile(".\build\main.obj", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0U, NULL); // print("ARG %u: %s\n", arg_index, argv[arg_index]); // u32 pathsize = SearchPathA(".", argv[arg_index], NULL, 4096U, filepath, NULL); // if (pathsize == 0U) { // for (u32 lib_index = 0U; lib_index < c_libs && pathsize == 0U; lib_index++) { // pathsize = SearchPathA(libs[lib_index], argv[arg_index], NULL, 4096U, filepath, NULL); // } // } // if (pathsize != 0U) { // print("FOUND: %s\n", filepath); // } else { // print("COULD NOT FOUND %s\n", argv[arg_index]); // continue; // } // HANDLE file = CreateFileA(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0U, NULL); // if (!file && file != INVALID_HANDLE_VALUE) { // continue; // } // LARGE_INTEGER file_size = { 0 }; // GetFileSizeEx(file, &file_size); // u8* buffer = VirtualAlloc(NULL, file_size.LowPart, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // DWORD bread = 0U; // ReadFile(file, buffer, file_size.LowPart, &bread, NULL); // if (*(u64*)buffer == lib_signature) { // read_lib(buffer + sizeof(lib_signature)); // } else { // read_COFF(buffer); // } // CloseHandle(file); // VirtualFree(buffer, 0U, MEM_RELEASE); // } // VirtualFree(filepath, 0U, MEM_RELEASE); // TODO: Reserve a huge block and commit when we need more memory u8* exe_buffer = VirtualAlloc(NULL, 4096U * 10U, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); HANDLE output_file = CreateFileA("./test.exe", GENERIC_WRITE, 0U, NULL, CREATE_ALWAYS, 0U, NULL); // loop through all OBJs header to find out which sections we need u8 sections_flag = 0U; u32 c_COFF_sections = 0U; struct COFF_sections* sections = read_COFFs_header(obj_files, c_obj_files, §ions_flag, &c_COFF_sections); __debugbreak(); // executable generation { struct PE_headers* headers = (struct PE_headers*)exe_buffer; IMAGE_SECTION_HEADER* section_table = (IMAGE_SECTION_HEADER*)(exe_buffer + sizeof(*headers)); u16 c_sections = 0U; // write the relevant sections if (sections_flag & TEXT_SECTION_FLAG) { // TODO: copy text to the relevant part of the buffer write_text_section_header(section_table, &c_sections); } if (sections_flag & DATA_SECTION_FLAG) { write_data_section_header(section_table, &c_sections); } if (sections_flag & BSS_SECTION_FLAG) { write_bss_section_header(section_table, &c_sections); } // Write section bodies // TODO: index per section ? u32 headers_size = ALIGN(sizeof(*headers) + (sizeof(IMAGE_SECTION_HEADER) * c_sections), FILE_ALIGNMENT); void* text_start = (void*)((u8*)headers + headers_size); for (u32 COFF_section_index = 0U; COFF_section_index < c_COFF_sections; COFF_section_index++) { struct COFF_sections const* cur_section = §ions[COFF_section_index]; if ( cur_section->text_section_off ) { mem_copy((void*)(cur_section->file_buffer + cur_section->text_section_off), text_start, cur_section->text_section_size); section_table[0U].SizeOfRawData = ALIGN(cur_section->text_section_size, FILE_ALIGNMENT); section_table[0U].Misc.VirtualSize = ALIGN(cur_section->text_section_size, SECTION_ALIGNMENT); } } // Set sections pointer to raw data _Static_assert(FILE_ALIGNMENT < SECTION_ALIGNMENT, "section alignment must greater or equal to file alignment in order to align headers_size on section alignment"); u32 file_offset = headers_size; u32 virtual_offset = ALIGN(headers_size, SECTION_ALIGNMENT); IMAGE_SECTION_HEADER* cur_section = section_table; for (u32 section_index = 0U; section_index < c_sections; section_index++, cur_section++) { cur_section->PointerToRawData = file_offset; cur_section->VirtualAddress = virtual_offset; ASSERT(cur_section->SizeOfRawData == ALIGN(cur_section->SizeOfRawData, FILE_ALIGNMENT)); ASSERT(cur_section->Misc.VirtualSize == ALIGN(cur_section->Misc.VirtualSize, SECTION_ALIGNMENT)); file_offset += cur_section->SizeOfRawData; virtual_offset += cur_section->Misc.VirtualSize; } // Fill header informations u32 addr_entry_point = section_table[0U].VirtualAddress; u32 base_of_code = section_table[0U].PointerToRawData; u32 size_of_code = section_table[0U].SizeOfRawData; u32 size_of_image = ALIGN(section_table[c_sections - 1U].VirtualAddress + section_table[c_sections - 1U].Misc.VirtualSize, SECTION_ALIGNMENT); write_exe_headers(headers, c_sections, addr_entry_point, base_of_code, size_of_code, headers_size, size_of_image); u32 bwrite = 0U; WriteFile(output_file, exe_buffer, headers->opt_header.SizeOfImage, &bwrite, NULL); ASSERT(bwrite == headers->opt_header.SizeOfImage); CloseHandle(output_file); } for (u32 COFF_section_index = 0U; COFF_section_index < c_COFF_sections; COFF_section_index++) { VirtualFree(sections[COFF_section_index].file_buffer, 0U, MEM_RELEASE); } VirtualFree(sections, 0U, MEM_RELEASE); VirtualFree(exe_buffer, 0U, MEM_RELEASE); VirtualFree(lib_path_env, 0U, MEM_RELEASE); VirtualFree(argv, 0U, MEM_RELEASE); VirtualFree(args_string, 0U, MEM_RELEASE); return 0; }