Hi,
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@theoretic.com
* 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):
http://lists.netsys.com/pipermail/full-disclosure/2004-January/015143.html
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.