Prototype for PE area preloader

Mike Hearn mike at
Fri Feb 13 18:50:21 CST 2004


Attached is a prototype of a technique we can use to solve the problems
caused by exec-shield and prelink. Yes, it's complicated. I wish it
wasn't, but simpler attempts at this haven't worked. I can't think of
another way to do it, given my limited knowledge. It's possible
Alexandre can figure something out, but as that hasn't happened yet here
is my contribution.

This is not useful inside Wine. It's a prototype only - it requires a
C99 compiler, the code is a mess, and all it shows is the loader app
running "target" which just prints out the mappings. Some more work
would be needed to turn this into a patch, but I don't know when I'll
get time to do this, so here is my work for others to pick up the baton
if they want to.

You can see if it's working by ensuring there is only one /dev/zero
entry every time when exec-shield-randomize is on. When it is on, the
output of "loader" should be different each time, use diff to see this.
When exec-shield is off, there should be no differences.

Prelink has the same effect but the addresses are randomized in a cron
job, not on every program load.

Don't worry if you don't fully understand this code, neither do I ;)
It's some pretty hardcore voodoo, at least by my standards.....

Here is the README contained within:

Mike Hearn <mike at>

* OK, so what is this?

This is a prototype of some code that will allow
us to solve the problems exec-shield and prelink are giving
Wine. They stop us working because they randomize the load addresses
of DSOs, so any linux libraries we link against might get dropped in
the middle of the load area we need. This is Bad News.

By default EXE files on Windows have their relocation records stripped
(to save space). They must be loaded at 0x400000. Because an EXE is
always the first thing into an address space, this is no
problem. Unfortunately on Wine of course the EXE isn't the first
thing into memory - Wine is.

Basically, the problem is that the dynamic linker thinks we have a
clear and empty address space. In fact we don't, we want a specific
region reserved for later use. The dynamic linker (rtld or "elf
interpreter") is the first thing that runs in a dynamically linked
binary, so we have a problem. Wine doesn't get control until the
damage is already done.

* How does it work?

We have a stub binary, the preloader, which is statically linked. It
gets control direct from the kernel, and the rtld isn't run.

We mmap the PE load area, with the MAP_FIXED (so we grab the exact
range of addresses we need) and MAP_NORESERVE (so we don't try and
allocate the memory, which is what scuppered earlier attempts to fix
the issue).

That's the easy part. It's, what, 4 lines of code? Nothing. It's
piffling. The hard part, which is what takes up the other 320 lines
of code is restarting the standard boot process. We must do what the
kernel would normally do - map in the target (wine-pthread/kthread),
relocate the sections to the right place, set up the entry stack for
the ELF interpreter and then jump into it in order to convert our
static binary, into a dynamic one.

Once we reach the other side of the dynamic linker rollercoaster, we
can then unmap the load area and tada! we now have a space we can map
PE binaries into.

This works, for a simple target binary. Will it work once we start
throwing more complex stuff (TLS initialization etc)? I just don't
know. Only time will tell. I expect so, but there's no way to be 100%
sure. This sort of stuff is simply not done anywhere else.

* Did you really write this all yourself?

Nope, I was aided by the sources to ul_exec (BSD licensed):

The rtld and kernel sources were valuable references but I didn't use
any code from them.

* How portable is it?

I only tested this on Linux. The problem only manifests itself on
Linux - other UNIXy platforms don't have technologise like
exec-shield (for security) or prelink (for speed), so it's not a
problem there. It's possible they will develop them in future though,
so portability is still a concern.

In theory it's 100% portable. ELF is a standard, and even though it
embedded asm and such, it should still work. MAP_NORESERVE *might* be
a GNU extension, I'm not sure, but I don't think it is. We can't work
reliably without this flag anyway so it's a moot point.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: execshield-prelink-0.1.tar.bz2
Type: application/x-bzip
Size: 6640 bytes
Desc: not available
Url :

More information about the wine-devel mailing list