aboutsummaryrefslogtreecommitdiff
path: root/winsup/cygwin/autoload.cc
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2013-04-23 09:44:36 +0000
committerCorinna Vinschen <corinna@vinschen.de>2013-04-23 09:44:36 +0000
commit61522196c71593da09572fce9af9e0d7dad61bc3 (patch)
tree9bf74facd67974fa2f780d6ce68b14eb7a94e371 /winsup/cygwin/autoload.cc
parent1875ee55d31d3673059373c8f9837bf98f93c713 (diff)
downloadnewlib-61522196c71593da09572fce9af9e0d7dad61bc3.zip
newlib-61522196c71593da09572fce9af9e0d7dad61bc3.tar.gz
newlib-61522196c71593da09572fce9af9e0d7dad61bc3.tar.bz2
* Merge in cygwin-64bit-branch.
Diffstat (limited to 'winsup/cygwin/autoload.cc')
-rw-r--r--winsup/cygwin/autoload.cc243
1 files changed, 211 insertions, 32 deletions
diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index 3e61f83..0c37942 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -32,34 +32,60 @@ bool NO_COPY wsock_started;
*
* So, immediately following the the call to one of the above routines
* we have:
- * DLL info (4 bytes) Pointer to a block of information concerning
+ * DLL info (4/8 bytes) Pointer to a block of information concerning
* the DLL (see below).
* DLL args (4 bytes) The number of arguments pushed on the stack by
* the call. If this is an odd value then this
* is a flag that non-existence of this function
* is not a fatal error
+ * func addr (8 bytes) (64 bit ONLY!)
+ * Address of the actual Win32 function. For the
+ * reason why this is necessary, see the below
+ * description of the load_state.
* func name (n bytes) asciz string containing the name of the function
* to be loaded.
*
* The DLL info block consists of the following
- * load_state (4 bytes) Pointer to a word containing the routine used
+ * load_state (4/8 bytes) Pointer to a word containing the routine used
* to eventually invoke the function. Initially
- * points to an init function which loads the
- * DLL, gets the process's load address,
- * changes the contents here to point to the
- * function address, and changes the call *(%eax)
- * to a jmp func. If the initialization has been
- * done, only the load part is done.
- * DLL handle (4 bytes) The handle to use when loading the DLL.
+ * points to an init function which loads the DLL,
+ * gets the process's load address, changes the contents
+ * here to point to the function address, and changes
+ * the address argument of the initial jmp call.
+ * On 64 bit, the jmp is not tweaked directly. Rather,
+ * the address of the Win32 function is stored in the
+ * aforementioned Win32 function address slot and fetched
+ * there for a jmp *%rax call. This indirection is
+ * necessary to workaround the lack of a jmp opcode with
+ * offset values > 32 bit. If the initialization has
+ * been done, only the load part is done.
+ * DLL handle (4/8 bytes) The handle to use when loading the DLL.
* DLL locker (4 bytes) Word to use to avoid multi-thread access during
* initialization.
- * extra init (4 bytes) Extra initialization function.
+ * extra init (4/8 bytes) Extra initialization function.
* DLL name (n bytes) asciz string containing the name of the DLL.
*/
/* LoadDLLprime is used to prime the DLL info information, providing an
additional initialization routine to call prior to calling the first
function. */
+#ifdef __x86_64__
+#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __asm__ (" \n\
+.ifndef " #dllname "_primed \n\
+ .section .data_cygwin_nocopy,\"w\" \n\
+ .align 8 \n\
+."#dllname "_info: \n\
+ .quad _std_dll_init \n\
+ .quad " #no_resolve_on_fork " \n\
+ .long -1 \n\
+ .align 8 \n\
+ .quad " #init_also " \n\
+ .string16 \"" #dllname ".dll\" \n\
+ .text \n\
+ .set " #dllname "_primed, 1 \n\
+.endif \n\
+");
+#else
#define LoadDLLprime(dllname, init_also, no_resolve_on_fork) __asm__ (" \n\
.ifndef " #dllname "_primed \n\
.section .data_cygwin_nocopy,\"w\" \n\
@@ -74,6 +100,7 @@ bool NO_COPY wsock_started;
.set " #dllname "_primed, 1 \n\
.endif \n\
");
+#endif
/* Create a "decorated" name */
#define mangle(name, n) #name "@" #n
@@ -88,6 +115,32 @@ bool NO_COPY wsock_started;
LoadDLLfuncEx3(name, n, dllname, notimp, err, 0)
/* Main DLL setup stuff. */
+#ifdef __x86_64__
+#define LoadDLLfuncEx3(name, n, dllname, notimp, err, no_resolve_on_fork) \
+ LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \
+ __asm__ (" \n\
+ .section ." #dllname "_autoload_text,\"wx\" \n\
+ .global " #name " \n\
+ .global _win32_" #name " \n\
+ .align 16 \n\
+" #name ": \n\
+_win32_" #name ": \n\
+ movq 3f(%rip),%rax \n\
+ jmp *%rax \n\
+1:movq 2f(%rip),%rax \n\
+ push %rbp # Keep 16 byte aligned \n\
+ push %r9 \n\
+ push %r8 \n\
+ push %rdx \n\
+ push %rcx \n\
+ call *(%rax) \n\
+2:.quad ." #dllname "_info \n\
+ .long (" #n "+" #notimp ") | (((" #err ") & 0xff) <<16) \n\
+3:.quad 1b \n\
+ .asciz \"" #name "\" \n\
+ .text \n\
+");
+#else
#define LoadDLLfuncEx3(name, n, dllname, notimp, err, no_resolve_on_fork) \
LoadDLLprime (dllname, dll_func_load, no_resolve_on_fork) \
__asm__ (" \n\
@@ -106,6 +159,7 @@ _win32_" mangle (name, n) ": \n\
.asciz \"" #name "\" \n\
.text \n\
");
+#endif
/* DLL loader helper functions used during initialization. */
@@ -121,6 +175,69 @@ extern "C" void dll_chain () __asm__ ("dll_chain");
extern "C" {
+#ifdef __x86_64__
+__asm__ (" \n\
+ .section .rdata,\"r\" \n\
+msg1: \n\
+ .ascii \"couldn't dynamically determine load address for '%s' (handle %p), %E\\0\"\n\
+ \n\
+ .text \n\
+ .p2align 4,,15 \n\
+noload: \n\
+ movq 40(%rsp),%rdx # Get the address of the information block\n\
+ movl 8(%rdx),%eax # Should we 'ignore' the lack \n\
+ test $1,%eax # of this function? \n\
+ jz 1f # Nope. \n\
+ andl $0xffff0000,%eax# upper word (== desired return value) \n\
+ movl %eax,32(%rsp) # Save for later (in shadow space) \n\
+ movl $127,%ecx # ERROR_PROC_NOT_FOUND \n\
+ call SetLastError # Set it \n\
+ movl 32(%rsp),%eax # Get back return value \n\
+ sarl $16,%eax # swap to low order word \n\
+ addq $40,%rsp # Revert stack \n\
+ pop %r10 # Drop pointer to 'return address' \n\
+ pop %rcx # Restore arg registers \n\
+ pop %rdx \n\
+ pop %r8 \n\
+ pop %r9 \n\
+ pop %rbp # ...and restore frame pointer \n\
+ ret # Return \n\
+1: \n\
+ movq (%rdx),%rax # Handle value \n\
+ movq 8(%rax),%r8 \n\
+ lea 20(%rdx),%rdx # Location of name of function \n\
+ lea msg1(%rip),%rcx # The message \n\
+ call api_fatal # Print message. Never returns \n\
+ \n\
+ .globl dll_func_load \n\
+dll_func_load: \n\
+ movq (%rsp),%rdx # 'Return address' contains load info \n\
+ movq (%rdx),%rcx # Where handle lives \n\
+ movq 8(%rcx),%rcx # Address of Handle to DLL \n\
+ addq $20,%rdx # Address of name of function to load \n\
+ subq $40,%rsp # Shadow space + 8 byte for alignment \n\
+ call GetProcAddress # Load it \n\
+ test %rax,%rax # Success? \n\
+ jne gotit # Yes \n\
+ jmp noload # Issue an error or return \n\
+gotit: \n\
+ addq $40,%rsp # Revert stack \n\
+ pop %r10 # Pointer to 'return address' \n\
+ movq %rax,12(%r10) # Move absolute address to address slot \n\
+ subq $25,%r10 # Point to jmp \n\
+ pop %rcx # Restore arg registers \n\
+ pop %rdx \n\
+ pop %r8 \n\
+ pop %r9 \n\
+ pop %rbp # ...and restore frame pointer \n\
+ jmp *%r10 # Jump to actual function \n\
+ \n\
+ .global dll_chain \n\
+dll_chain: \n\
+ push %rax # Restore 'return address' \n\
+ jmp *%rdx # Jump to next init function \n\
+");
+#else
__asm__ (" \n\
.text \n\
msg1: \n\
@@ -177,13 +294,14 @@ dll_chain: \n\
pushl %eax # Restore 'return address' \n\
jmp *%edx # Jump to next init function \n\
");
+#endif
/* C representations of the two info blocks described above.
FIXME: These structures confuse gdb for some reason. GDB can print
the whole structure but has problems with the name field? */
struct dll_info
{
- DWORD load_state;
+ UINT_PTR load_state;
HANDLE handle;
LONG here;
void (*init) ();
@@ -194,14 +312,22 @@ struct func_info
{
struct dll_info *dll;
LONG decoration;
+#ifdef __x86_64__
+ UINT_PTR func_addr;
+#endif
char name[];
};
/* Mechanism for setting up info for passing to dll_chain routines. */
+#ifdef __x86_64__
+typedef __uint128_t two_addr_t;
+#else
+typedef __uint64_t two_addr_t;
+#endif
union retchain
{
- struct {long high; long low;};
- long long ll;
+ struct {uintptr_t high; uintptr_t low;};
+ two_addr_t ll;
};
@@ -228,10 +354,56 @@ dll_load (HANDLE& handle, WCHAR *name)
#define RETRY_COUNT 10
/* The standard DLL initialization routine. */
-__attribute__ ((used, noinline)) static long long
+#ifdef __x86_64__
+
+/* On x86_64, we need assembler wrappers for std_dll_init and wsock_init.
+ In the x86_64 ABI it's no safe bet that frame[1] (aka 8(%rbp)) contains
+ the return address. Consequentially, if we try to overwrite frame[1]
+ with the address of dll_chain, we end up with a scrambled stack, the
+ result depending on the optimization settings and the current frame of
+ mind of the compiler. So for x86_64, we disable overwriting the return
+ address in the real std_dll_init/wsock_init function, but rather do this
+ in the wrapper, after return from the function, when we exactly know
+ where the original return address is stored on the stack. */
+
+#define INIT_WRAPPER(func) \
+__asm__ (" \n\
+ .text \n\
+ .p2align 4,,15 \n\
+ .seh_proc _" #func " \n\
+_" #func ": \n\
+ pushq %rbp \n\
+ .seh_pushreg %rbp \n\
+ movq %rsp,%rbp \n\
+ .seh_setframe %rbp,0 \n\
+ subq $0x20,%rsp \n\
+ .seh_stackalloc 32 \n\
+ .seh_endprologue \n\
+ movq 0x28(%rsp),%rcx # return address as parameter \n\
+ call " #func " \n\
+ movdqa %xmm0,0x10(%rsp) # 128 bit return value in xmm0 \n\
+ movq 0x10(%rsp),%rax # copy over to %rax and %rdx \n\
+ movq 0x18(%rsp),%rdx \n\
+ leaq dll_chain(%rip),%rcx # load address of dll_chain \n\
+ movq %rcx,0x28(%rsp) # and overwrite return address \n\
+ addq $0x20,%rsp \n\
+ popq %rbp \n\
+ ret \n\
+ .seh_endproc \n\
+");
+
+INIT_WRAPPER (std_dll_init)
+
+__attribute__ ((used, noinline)) static two_addr_t
+std_dll_init (struct func_info *func)
+#else
+__attribute__ ((used, noinline)) static two_addr_t
std_dll_init ()
+#endif
{
+#ifndef __x86_64__
struct func_info *func = (struct func_info *) __builtin_return_address (0);
+#endif
struct dll_info *dll = func->dll;
retchain ret;
@@ -284,26 +456,38 @@ std_dll_init ()
}
/* Set "arguments" for dll_chain. */
- ret.low = (long) dll->init;
- ret.high = (long) func;
+ ret.low = (uintptr_t) dll->init;
+ ret.high = (uintptr_t) func;
InterlockedDecrement (&dll->here);
+#ifndef __x86_64__
/* Kludge alert. Redirects the return address to dll_chain. */
- __asm__ __volatile__ (" \n\
- movl $dll_chain,4(%ebp) \n\
- ");
+ uintptr_t *volatile frame = (uintptr_t *) __builtin_frame_address (0);
+ frame[1] = (uintptr_t) dll_chain;
+#endif
return ret.ll;
}
/* Initialization function for winsock stuff. */
WSADATA NO_COPY wsadata;
-static long long __attribute__ ((used, noinline))
+
+#ifdef __x86_64__
+/* See above comment preceeding std_dll_init. */
+INIT_WRAPPER (wsock_init)
+
+__attribute__ ((used, noinline)) static two_addr_t
+wsock_init (struct func_info *func)
+#else
+__attribute__ ((used, noinline)) static two_addr_t
wsock_init ()
+#endif
{
static LONG NO_COPY here = -1L;
+#ifndef __x86_64__
struct func_info *func = (struct func_info *) __builtin_return_address (0);
+#endif
struct dll_info *dll = func->dll;
while (InterlockedIncrement (&here))
@@ -330,23 +514,23 @@ wsock_init ()
debug_printf ("szSystemStatus %s", wsadata.szSystemStatus);
debug_printf ("iMaxSockets %d", wsadata.iMaxSockets);
debug_printf ("iMaxUdpDg %d", wsadata.iMaxUdpDg);
- debug_printf ("lpVendorInfo %d", wsadata.lpVendorInfo);
wsock_started = 1;
}
}
+#ifndef __x86_64__
/* Kludge alert. Redirects the return address to dll_chain. */
- __asm__ __volatile__ (" \n\
- movl $dll_chain,4(%ebp) \n\
- ");
+ uintptr_t *volatile frame = (uintptr_t *) __builtin_frame_address (0);
+ frame[1] = (uintptr_t) dll_chain;
+#endif
InterlockedDecrement (&here);
volatile retchain ret;
/* Set "arguments for dll_chain. */
- ret.low = (long) dll_func_load;
- ret.high = (long) func;
+ ret.low = (uintptr_t) dll_func_load;
+ ret.high = (uintptr_t) func;
return ret.ll;
}
@@ -383,19 +567,14 @@ LoadDLLfunc (ReportEventW, 36, advapi32)
LoadDLLfunc (DnsQuery_A, 24, dnsapi)
LoadDLLfunc (DnsRecordListFree, 8, dnsapi)
-// 50 = ERROR_NOT_SUPPORTED. Returned if OS doesn't support iphlpapi funcs
-LoadDLLfuncEx2 (GetAdaptersAddresses, 20, iphlpapi, 1, 50)
+LoadDLLfunc (GetAdaptersAddresses, 20, iphlpapi)
LoadDLLfunc (GetIfEntry, 4, iphlpapi)
LoadDLLfunc (GetIpAddrTable, 12, iphlpapi)
LoadDLLfunc (GetIpForwardTable, 12, iphlpapi)
LoadDLLfunc (GetNetworkParams, 8, iphlpapi)
LoadDLLfunc (GetUdpTable, 12, iphlpapi)
-LoadDLLfuncEx (AttachConsole, 4, kernel32, 1)
-LoadDLLfuncEx (GetModuleHandleExW, 12, kernel32, 1)
LoadDLLfuncEx (GetNamedPipeClientProcessId, 8, kernel32, 1)
-LoadDLLfuncEx (GetSystemWow64DirectoryW, 8, kernel32, 1)
-LoadDLLfuncEx (GetVolumePathNamesForVolumeNameW, 16, kernel32, 1)
LoadDLLfunc (LocaleNameToLCID, 8, kernel32)
LoadDLLfunc (WNetCloseEnum, 4, mpr)