On 10/07/2026 17:20, Lee via Cygwin wrote:
On Fri, Jul 10, 2026 at 8:31 AM Nikhil Sabharwal via Cygwin
<[email protected]> wrote:
To the developers & maintainers of cygwin
I wanted to reach out regarding Cygwin and its compatibility with Windows
exploit protection mechanisms, specifically ASLR. My current understanding is
that MSYS2 has difficulties supporting this feature due to limitations
inherited from Cygwin. Certain functionality within the compatibility layer
appears to rely on a fixed address layout, causing binaries with ASLR enabled
to fail with error messages such as:
*** fatal error - cygheap base mismatch detected - 0xD5F410/0xE9F410
These are the results I observe when running binaries built with the
DYNAMICBASE flag enabled.
This seems to work around the problem. Note: I copied the solution
from stackoverflow with no real understanding on my part, so I can't
explain what's going on
LDFLAGS="${LDFLAGS} -Wl,--dynamicbase,--export-all-symbols"
# https://en.wikipedia.org/wiki/Address_space_layout_randomization
#
https://stackoverflow.com/questions/24283918/how-can-i-enable-aslr-dep-and-safeseh-on-an-exe-in-codeblocks-using-mingw
# ASLR with gcc has a problem: -Wl,--dynamicbase doesn't emit the
necessary relocation table.
# As a workaround, you can pass -Wl,--dynamicbase,--export-all-symbols
# NOTE: you can't have both this and profiling (cflags='-pg') enabled!
Telling GCC to "export" all symbols doesn't really do anything relevant
except abuse a side effect of how GCC implements that option.
Note: This gcc/binutils bug mostly traces back to the gcc/binutils team
previously misunderstanding the PE file format design, and thus trying to
shoehorn it into concepts from a.out and other old POSIX file formats.
The PE file format combines 4 INDEPENDENT and OVERLAPPING types of
conceptually
simple relocations:
1. COFF .o files (alone or in .a files) can contain any relocation
imaginable
with any level of complexity. Via clever combinations of the
available COFF
relocation types and section flags, any language feature (such as C++
templates or whatever may be needed for newer languages) can be expressed
such that any standard PE linker will pick the correct .o files from .a
libraries, build up internal tables and reduce the combined EXE or
DLL to do
everything in terms of the 3 simpler relocation types. Among the things
that can be done with this is to do things like reference the in memory
address of the PE file headers (which is always memory mapped just before
the first declared address space, as the entire PE file is
essentially just
mmap()-ed into the process address space with slight adjustments of
memory
protection according to the header).
2. True PIC code addresses symbols elsewhere in the PE file relative to the
instruction pointer (with the linker doing the precalculations as .o
files
are combined). For x86_32, this is mostly used with the relative jump
instructions such as call, jmp and conditional jump. If necessary,
code can
use well known trickery to copy the instruction pointer into a general
register that can be referenced in the address fields of other
instructions.
For x86_64 and ARM, the instruction pointer itself can be referenced
directly. For some old RISC architectures like PowerPC, all but a few
lines of assembly code will have the actual load address loaded into a
specific general register dictated by the calling convention and calls
between PE files use special instruction sequences to load that register.
None of this involves a Unix-derived "PLT" table or other such
complications. The Compiler generates instruction sequences that
reference the target symbol directly using appropriate addressing modes,
the assembler generates symbol references and constant offsets based on
instruction sizes (for example jump relative to (__foo - (address of the
field set to __foo) - (how much the instruction adds to the field to
choose the jump destination)), the linker does the subtractions leaving
behind only the constant difference between where this instruction is
and where __foo is. Loading the PE file at a different offset (due to
address space conflicts or ASLR, it doesn't matter) will cancel out
with itself, keeping the code position independent.
3. "Base relocations" in PE files (DLL, EXE, EFI, Driver or anything else)
are a sorted list of all the locations in the file that will need to be
adjusted if the file is loaded at a memory offset which is different from
the "preferred" address listed in the file header. However the actual
load address will always be a multiple of the "section alignment" power
of 2 listed in the file header.
It is the job of the linker (GNU ln or MS link) to reduce all internal
relocation needs to the very limited number of relocation types that fit
in this table format for the target architecture (x86_32 and x86_64 only
support adding the difference between actual and preferred load address
to pointer sized unaligned places in the image, other architectures have
additional types to deal with instructions that load addresses in
High/Low halves).
These relocations are not done at load time, but are instead done
inside the memory mapping machinery in the kernel as each memory page
is (re)loaded from the PE file, hence why the format must be followed
exactly to allow the kernel to find the table entries via random access.
"Base relocations" are the mechanism responsible for ASLR compatibility
and this part of the format is unchanged since 1992 (except for the
introduction of new CPU architectures and a single informative bit
telling the loader that ASLR is not a problem).
4. The "IAT" is a single array (not a list of arbitrary code locations as
some GNU folk think) that is almost a PLT, which is filled at load time
with the absolute addresses of symbols from other PE files. Compiler
generated code will reference the IAT entry pointing to imported symbol
_foo as __imp__foo and use CPU addressing modes that use that pointer
like any other read only pointer variable, for example if _foo is a
function, the compiler emits an instruction to call (*__imp__foo),
using PIC or base relocations to reach __imp__foo. If _foo is a
variable the compiler emits code that references (*__imp__foo) in
similar ways. If _foo is a function and the compiler accidentally
emits a normal internal call to symbol _foo, the linker generates a
tiny stub named _foo which forwards the call to *__imp__foo. That
stub trick won't work if _foo is a variable, and the linker is
supposed to refuse linking this mistake into a PE file.
In C/C++/Objective C, marking an external symbol _foo in a header
or code with __attribute__((dllimport)) tells the compiler that
every syntactical reference to the symbol __foo is really a
reference to the hidden pointer __imp__foo when generating code.
Enjoy
Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
Transformervej 29, 2860 Søborg, Denmark. Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded
--
Problem reports: https://cygwin.com/problems.html
FAQ: https://cygwin.com/faq/
Documentation: https://cygwin.com/docs.html
Unsubscribe info: https://cygwin.com/ml/#unsubscribe-simple