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.
This commit is contained in:
Mounir IDRASSI 2026-05-15 18:58:28 +09:00
parent 37b7247796
commit 1a54e47dda
4 changed files with 83 additions and 17 deletions

View File

@ -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=")) {

View File

@ -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.

11
tcc.h
View File

@ -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;

40
tccpe.c
View File

@ -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);