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

Reply via email to