[PATCH] loader: On Mac OS, don't use external symbols for the DOS and shared heap segments.
cdavis at mymail.mines.edu
Sun Jun 10 11:47:55 CDT 2012
In Clang-built optimized Wine, the environ(3) pointer was somehow getting set
to NULL. Strangely, any watchpoint I set on it wouldn't trip. This led me to
believe that some sort of virtual memory black magic was going on.
Eventually I discovered that black magic: a call to
NtAllocateVirtualMemory(). During process startup,
NtAllocateVirtualMemory() first tries the reserved areas if they haven't
been taken yet. When I examined the reserved areas list with a debugger,
I found out that two of the reserved areas actually covered the wine
executable image itself--and not the special segments that were
declared with the .zerofill assembler directive.
So, I looked at the assembly produced by Clang at -O0, -O1, and -O2 for
the wine loader, and discovered that at -O2, Clang will actually optimize
away the initial reserved areas table. Because of this, the
pre-allocated chunks of memory--which were declared as external arrays
of 'char'--were now being accessed directly. Because the arrays were
declared extern, the compiler emitted a "non-lazy symbol pointer" to
the arrays, and the arrays were accessed through that.
The problem only happened when the symbols weren't marked global
(i.e. with '.globl'). So I inspected the indirect symbol table
produced by the assembler. I discovered that, in this case, the
non-lazy symbol pointers were just marked "local". Apparently,
if there's even the possibility that a symbol could be stripped
from the final executable, the assembler just gives up when it
tries to emit an indirect symbol for it. On Mac OS, "local"
indirect symbols have no actual value--probably because they should
never happen. So, the linker just made them point to the text segment.
My first idea was to just add some '.globl' directives in the code. But
then I asked myself: why are we using an indirect symbol at all? Why are
we treating this like an external symbol when it clearly isn't? So now,
the arrays are declared static, and their owning section is explicitly
declared to the C compiler. While compile times have gone up, at least
the compiled loader executable actually works.
loader/main.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/loader/main.c b/loader/main.c
index edbf4f7..8fbbf74 100644
@@ -43,9 +43,10 @@
-__asm__(".zerofill WINE_DOS, WINE_DOS, ___wine_dos, 0x40000000");
-__asm__(".zerofill WINE_SHAREDHEAP, WINE_SHAREDHEAP, ___wine_shared_heap, 0x03000000");
-extern char __wine_dos[0x40000000], __wine_shared_heap[0x03000000];
+__asm__(".zerofill WINE_DOS, WINE_DOS");
+__asm__(".zerofill WINE_SHAREDHEAP, WINE_SHAREDHEAP");
+static char __wine_dos[0x40000000] __attribute__((section("WINE_DOS,WINE_DOS")));
+static char __wine_shared_heap[0x03000000] __attribute__((section("WINE_SHAREDHEAP,WINE_SHAREDHEAP")));
static const struct wine_preload_info wine_main_preload_info =
More information about the wine-patches