From 1a54e47dda4722e849a83b2fb5108a6ee7bb8725 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Fri, 15 May 2026 18:58:28 +0900 Subject: [PATCH] pe: add dll characteristic linker options Add PE linker switches to set and clear DYNAMIC_BASE, NX_COMPAT, HIGH_ENTROPY_VA and TERMINAL_SERVER_AWARE without changing x86/x64 defaults. HIGH_ENTROPY_VA is limited to 64-bit PE targets and implies DYNAMIC_BASE; clearing DYNAMIC_BASE also clears HIGH_ENTROPY_VA. When DYNAMIC_BASE is requested for an executable, emit base relocations and clear RELOCS_STRIPPED so Windows ASLR can relocate the image. Keep the existing ARM64 default hardening flags. --- libtcc.c | 40 +++++++++++++++++++++++++++++++++++++++- tcc-doc.texi | 9 +++++++++ tcc.h | 11 +++++++++++ tccpe.c | 40 ++++++++++++++++++++++++---------------- 4 files changed, 83 insertions(+), 17 deletions(-) diff --git a/libtcc.c b/libtcc.c index 99c8b9c9..1de825b3 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1406,6 +1406,20 @@ succ: static void args_parser_add_file(TCCState *s, const char* filename, int filetype); +#ifdef TCC_TARGET_PE +static void tcc_pe_set_dll_characteristics(TCCState *s, unsigned flags) +{ + s->pe_dll_characteristics |= flags; + s->pe_dll_characteristics_clear &= ~flags; +} + +static void tcc_pe_clear_dll_characteristics(TCCState *s, unsigned flags) +{ + s->pe_dll_characteristics &= ~flags; + s->pe_dll_characteristics_clear |= flags; +} +#endif + /* set linker options */ static int tcc_set_linker(TCCState *s, const char *optarg) { @@ -1475,7 +1489,31 @@ static int tcc_set_linker(TCCState *s, const char *optarg) s->znodelete = 1; #ifdef TCC_TARGET_PE } else if (link_option(&o, "large-address-aware")) { - s->pe_characteristics |= 0x20; + s->pe_characteristics |= PE_IMAGE_FILE_LARGE_ADDRESS_AWARE; + } else if (link_option(&o, "dynamicbase")) { + tcc_pe_set_dll_characteristics(s, PE_DLLCHARACTERISTICS_DYNAMIC_BASE); + } else if (link_option(&o, "disable-dynamicbase|no-dynamicbase")) { + tcc_pe_clear_dll_characteristics(s, + PE_DLLCHARACTERISTICS_DYNAMIC_BASE | + PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA); + } else if (link_option(&o, "nxcompat")) { + tcc_pe_set_dll_characteristics(s, PE_DLLCHARACTERISTICS_NX_COMPAT); + } else if (link_option(&o, "disable-nxcompat|no-nxcompat")) { + tcc_pe_clear_dll_characteristics(s, PE_DLLCHARACTERISTICS_NX_COMPAT); + } else if (link_option(&o, "high-entropy-va")) { +# if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64) + tcc_pe_set_dll_characteristics(s, + PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | + PE_DLLCHARACTERISTICS_DYNAMIC_BASE); +# else + goto err; +# endif + } else if (link_option(&o, "disable-high-entropy-va|no-high-entropy-va")) { + tcc_pe_clear_dll_characteristics(s, PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA); + } else if (link_option(&o, "tsaware")) { + tcc_pe_set_dll_characteristics(s, PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE); + } else if (link_option(&o, "disable-tsaware|no-tsaware")) { + tcc_pe_clear_dll_characteristics(s, PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE); } else if (link_option(&o, "file-alignment=")) { s->pe_file_align = strtoul(o.arg, &end, 16); } else if (link_option(&o, "stack=")) { diff --git a/tcc-doc.texi b/tcc-doc.texi index 9a9695f7..22d6d0a0 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -386,6 +386,15 @@ Set type for PE (Windows) executables. @item -Wl,-[Ttext=# | section-alignment=# | file-alignment=# | image-base=# | stack=#] Modify executable layout. +@item -Wl,-[dynamicbase | nxcompat | high-entropy-va | tsaware] +@item -Wl,-[no-dynamicbase | no-nxcompat | no-high-entropy-va | no-tsaware] +@item -Wl,-[disable-dynamicbase | disable-nxcompat | disable-high-entropy-va | disable-tsaware] +Set or clear PE (Windows) executable header hardening flags. The +@option{-Wl,-high-entropy-va} option is supported on x86-64 and ARM64 PE +targets and implies @option{-Wl,-dynamicbase}. Clearing dynamicbase also +clears high-entropy-va. When @option{-Wl,-dynamicbase} is used for an +executable, TCC also enables base relocation emission for Windows ASLR. + @item -Wl,-Bsymbolic Set DT_SYMBOLIC tag. diff --git a/tcc.h b/tcc.h index 24713356..c74fae47 100644 --- a/tcc.h +++ b/tcc.h @@ -727,6 +727,15 @@ struct sym_attr { #endif }; +#ifdef TCC_TARGET_PE +#define PE_IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define PE_IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +#define PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 +#define PE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define PE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 +#define PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 +#endif + struct TCCState { unsigned char verbose; /* if true, display some information during compilation */ unsigned char nostdinc; /* if true, no standard headers are added */ @@ -939,6 +948,8 @@ struct TCCState { /* PE info */ int pe_subsystem; unsigned pe_characteristics; + unsigned pe_dll_characteristics; + unsigned pe_dll_characteristics_clear; unsigned pe_file_align; unsigned pe_stack_size; addr_t pe_imagebase; diff --git a/tccpe.c b/tccpe.c index fe76b433..2304c99e 100644 --- a/tccpe.c +++ b/tccpe.c @@ -238,16 +238,16 @@ typedef struct _IMAGE_BASE_RELOCATION { #define IMAGE_SIZEOF_BASE_RELOCATION 8 #ifndef IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA -#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 +#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA #endif #ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE -#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE PE_DLLCHARACTERISTICS_DYNAMIC_BASE #endif #ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT -#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 +#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT PE_DLLCHARACTERISTICS_NX_COMPAT #endif #ifndef IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE -#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 +#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE #endif #define IMAGE_REL_BASED_ABSOLUTE 0 @@ -275,6 +275,21 @@ typedef struct _IMAGE_BASE_RELOCATION { #endif /* ndef IMAGE_NT_SIGNATURE */ /* ----------------------------------------------------------- */ +static WORD pe_get_dll_characteristics(TCCState *s1) +{ + unsigned v = 0; + +#ifdef TCC_TARGET_ARM64 + v = PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | + PE_DLLCHARACTERISTICS_DYNAMIC_BASE | + PE_DLLCHARACTERISTICS_NX_COMPAT | + PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE; +#endif + v |= s1->pe_dll_characteristics; + v &= ~s1->pe_dll_characteristics_clear; + return v; +} + #ifndef IMAGE_FILE_MACHINE_ARM64 #define IMAGE_FILE_MACHINE_ARM64 0xAA64 #endif @@ -725,14 +740,7 @@ static int pe_write(struct pe_info *pe) 0x00000200, /*DWORD SizeOfHeaders; */ 0x00000000, /*DWORD CheckSum; */ 0x0002, /*WORD Subsystem; */ -#if defined(TCC_TARGET_ARM64) - IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | - IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | - IMAGE_DLLCHARACTERISTICS_NX_COMPAT | - IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, -#else 0x0000, /*WORD DllCharacteristics; */ -#endif 0x00100000, /*DWORD SizeOfStackReserve; */ 0x00001000, /*DWORD SizeOfStackCommit; */ 0x00100000, /*DWORD SizeOfHeapReserve; */ @@ -853,11 +861,14 @@ static int pe_write(struct pe_info *pe) pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders; pe_header.opthdr.ImageBase = pe->imagebase; pe_header.opthdr.Subsystem = pe->subsystem; + pe_header.opthdr.DllCharacteristics = pe_get_dll_characteristics(s1); if (s1->pe_stack_size) pe_header.opthdr.SizeOfStackReserve = s1->pe_stack_size; if (PE_DLL == pe->type) pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL; pe_header.filehdr.Characteristics |= s1->pe_characteristics; + if (pe->reloc) + pe_header.filehdr.Characteristics &= ~PE_IMAGE_FILE_RELOCS_STRIPPED; if (pe->coffsym) { pe_add_coffsym(pe); @@ -1280,11 +1291,8 @@ static int pe_assign_addresses (struct pe_info *pe) Section *s; TCCState *s1 = pe->s1; - if (PE_DLL == pe->type -#ifdef TCC_TARGET_ARM64 - || PE_EXE == pe->type || PE_GUI == pe->type -#endif - ) + if (PE_DLL == pe->type || + (pe_get_dll_characteristics(s1) & PE_DLLCHARACTERISTICS_DYNAMIC_BASE)) pe->reloc = new_section(s1, ".reloc", SHT_PROGBITS, 0); //pe->thunk = new_section(s1, ".iedat", SHT_PROGBITS, SHF_ALLOC);