[Bug 33159] Loading a .dll without relocations fails under Mac OS X (needs preloader)

wine-bugs at winehq.org wine-bugs at winehq.org
Mon Jan 19 22:44:44 CST 2015


https://bugs.winehq.org/show_bug.cgi?id=33159

Ken Thomases <ken at codeweavers.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ken at codeweavers.com

--- Comment #15 from Ken Thomases <ken at codeweavers.com> ---
Here's my understanding of the issue:

* On OS X, Wine reserves the WINE_DOS area from 0x00001000 to 0x40001000 by
defining a zero-fill segment in the loader executable.  It also reserves the
WINE_SHAREDHEAP area from 0x7f000000 to 0x82000000, but that's not particularly
relevant in this case.

* The game's Activation.dll can only be loaded at 0x40000000 to 0x4010D000.

* It would not actually be a problem if the DLL's address range were completely
inside Wine's reserved area nor completely outside it.  The problem is that it
straddles the boundary.

* The size of the WINE_DOS area was chosen somewhat arbitrarily.  It could be
adjusted to accommodate this particular DLL, but that is as likely to cause
problems for other DLLs as fix them.

* On Linux, when running 32-bit Wine, the preloader reserves:

    0x00000000 - 0x00010000 low 64K
    0x00010000 - 0x00110000 DOS Area
    0x00110000 - 0x68000000 low-memory area
    0x7f000000 - 0x82000000 top-down allocations + shared heap + virtual heap

plus whatever address range the main executable needs to load into.

As you can see, this includes everything that's reserved on OS X and
substantially more.

* The difference comes in because, when ntdll has finished loading and
initializing the app, it calls virtual_release_address_space() and that
unreserves much of that area on Linux but not OS X.  (Things are somewhat
different for large-address-space-aware apps.)

This can't be done on OS X because the reserved areas are "owned" by dyld, the
dynamic loader.  It wouldn't be a problem if these unreserved areas were used
for loading a Windows-style DLL.  But once they are unreserved, they could be
used to load platform-native dynamic libraries.  Since loading those is a job
for dyld and it thought it already owned that memory and dedicated it to the
segments of the main executable, it freaks out if a new library is loaded into
that same address range.

---

I have looked into trying to find ways to convince the kernel and/or dyld to
map the ranges we want reserved early in the process lifetime but leave them
out of dyld's record-keeping so it wouldn't freak out if they later get used.

At one point, I thought of including an LC_SEGMENT_64 load command, that's
normally only used in 64-bit images, in our 32-bit loader.  At the time, it
seemed that the kernel would map it but dyld would ignore it.  However, both
the kernel and dyld have tightened up their sanity checking, probably for
security, and refuse to load such an executable.


Unfortunately, I think that it probably won't be feasible to implement a
preloader on OS X, either.  The kernel and dyld are fairly intimately
intertwined.  Loading an executable is a complicated process that's split
between them.  The source code for both is theoretically open, but I'm pretty
sure that dyld's is incomplete.  (For example, it seems to have some capability
to load PE executables, but the code for that is not in the source base.)

Also, any preloader would have to do mmap() or mach_vm_allocate() calls to
reserve the desired address ranges in a way that they could be subsequently
released.  On Linux, the preloader just uses a syscall to do the mmap() call. 
However, on OS X, the syscall interface is not the public interface and Apple
does not commit to maintaining binary compatibility across versions of the OS. 
Instead, Apple states that the system library interfaces are the public and
stable interfaces.  But we need to reserve our address ranges before the system
libraries are initialized because they will use those ranges if they get a
chance.  So, there's no good way to actually map/reserve the address ranges we
want.


This inability to reliably use mmap() without loading the system library also
defeats another scheme I had considered.  I was thinking that we could craft a
dynamic library that didn't link with _anything_ else.  It would be the first
library in the loader's dependencies.  It would have an initializer function
that would reserve the desired address ranges.  That could theoretically be run
before the initializers of the system library.  Except it can't, because it
would have to mmap() and that a) can't be done until after the system library
is initialized, and b) would create a dependency on the system library that
would force it to initialize first, anyway.

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list