aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Drake <cygwin@jdrake.com>2025-03-21 13:45:22 -0700
committerJeremy Drake <github@jdrake.com>2025-03-31 13:50:12 -0700
commit3754840d7fc71e8d973ee40c2d1d101f826864ed (patch)
tree719c8f64b3440f0805fd8575fcd39d57228f084a
parent6e75829e117f5daf73b8a505284a58faa87776fa (diff)
downloadnewlib-3754840d7fc71e8d973ee40c2d1d101f826864ed.zip
newlib-3754840d7fc71e8d973ee40c2d1d101f826864ed.tar.gz
newlib-3754840d7fc71e8d973ee40c2d1d101f826864ed.tar.bz2
Cygwin: factor out find_fast_cwd_pointer to arch-specific file.
This is in preparation for rewriting it using udis86, and adding an implementation for aarch64 hosts. Signed-off-by: Jeremy Drake <cygwin@jdrake.com>
-rw-r--r--winsup/cygwin/Makefile.am1
-rw-r--r--winsup/cygwin/path.cc122
-rw-r--r--winsup/cygwin/x86_64/fastcwd.cc128
3 files changed, 134 insertions, 117 deletions
diff --git a/winsup/cygwin/Makefile.am b/winsup/cygwin/Makefile.am
index d47a1a2..a0e8397 100644
--- a/winsup/cygwin/Makefile.am
+++ b/winsup/cygwin/Makefile.am
@@ -52,6 +52,7 @@ LIB_NAME=libcygwin.a
if TARGET_X86_64
TARGET_FILES= \
x86_64/bcopy.S \
+ x86_64/fastcwd.cc \
x86_64/memchr.S \
x86_64/memcpy.S \
x86_64/memmove.S \
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index d2aaed3..3a5e2ee 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -4490,122 +4490,10 @@ fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir)
f_cwd->DirectoryHandle = dir;
}
-/* This function scans the code in ntdll.dll to find the address of the
- global variable used to access the CWD. While the pointer is global,
- it's not exported from the DLL, unfortunately. Therefore we have to
- use some knowledge to figure out the address. */
-
-#define peek32(x) (*(int32_t *)(x))
-
-static fcwd_access_t **
-find_fast_cwd_pointer ()
-{
- /* Fetch entry points of relevant functions in ntdll.dll. */
- HMODULE ntdll = GetModuleHandle ("ntdll.dll");
- if (!ntdll)
- return NULL;
- const uint8_t *get_dir = (const uint8_t *)
- GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
- const uint8_t *ent_crit = (const uint8_t *)
- GetProcAddress (ntdll, "RtlEnterCriticalSection");
- if (!get_dir || !ent_crit)
- return NULL;
- /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
- const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 80);
- if (!rcall)
- return NULL;
- /* Fetch offset from instruction and compute address of called function.
- This function actually fetches the current FAST_CWD instance and
- performs some other actions, not important to us. */
- const uint8_t *use_cwd = rcall + 5 + peek32 (rcall + 1);
- /* Next we search for the locking mechanism and perform a sanity check.
- On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
- Windows 8 does not call RtlEnterCriticalSection. The code manipulates
- the FastPebLock manually, probably because RtlEnterCriticalSection has
- been converted to an inline function. Either way, we test if the code
- uses the FastPebLock. */
- const uint8_t *movrbx;
- const uint8_t *lock = (const uint8_t *)
- memmem ((const char *) use_cwd, 80,
- "\xf0\x0f\xba\x35", 4);
- if (lock)
- {
- /* The lock instruction tweaks the LockCount member, which is not at
- the start of the PRTL_CRITICAL_SECTION structure. So we have to
- subtract the offset of LockCount to get the real address. */
- PRTL_CRITICAL_SECTION lockaddr =
- (PRTL_CRITICAL_SECTION) (lock + 9 + peek32 (lock + 4)
- - offsetof (RTL_CRITICAL_SECTION, LockCount));
- /* Test if lock address is FastPebLock. */
- if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
- return NULL;
- /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
- address of the current fcwd_access_t pointer, and it should be pretty
- near to the locking stuff. */
- movrbx = (const uint8_t *) memmem ((const char *) lock, 40,
- "\x48\x8b\x1d", 3);
- }
- else
- {
- /* Usually the callq RtlEnterCriticalSection follows right after
- fetching the lock address. */
- int call_rtl_offset = 7;
- /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
- %rcx for the subsequent RtlEnterCriticalSection call. */
- lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
- "\x48\x8d\x0d", 3);
- if (!lock)
- {
- /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
- ops, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
- lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
- "\x4c\x8d\x25", 3);
- call_rtl_offset = 14;
- }
-
- if (!lock)
- {
- /* A recent Windows 11 Preview calls `lea rel(rip),%r13' then
- some unrelated instructions, then `callq RtlEnterCriticalSection'.
- */
- lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
- "\x4c\x8d\x2d", 3);
- call_rtl_offset = 24;
- }
-
- if (!lock)
- {
- return NULL;
- }
-
- PRTL_CRITICAL_SECTION lockaddr =
- (PRTL_CRITICAL_SECTION) (lock + 7 + peek32 (lock + 3));
- /* Test if lock address is FastPebLock. */
- if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
- return NULL;
- /* Next is the `callq RtlEnterCriticalSection'. */
- lock += call_rtl_offset;
- if (lock[0] != 0xe8)
- return NULL;
- const uint8_t *call_addr = (const uint8_t *)
- (lock + 5 + peek32 (lock + 1));
- if (call_addr != ent_crit)
- return NULL;
- /* In contrast to the above Windows 8 code, we don't have to search
- for the `mov rel(%rip),%rbx' instruction. It follows right after
- the call to RtlEnterCriticalSection. */
- movrbx = lock + 5;
- }
- if (!movrbx)
- return NULL;
- /* Check that the next instruction tests if the fetched value is NULL. */
- const uint8_t *testrbx = (const uint8_t *)
- memmem (movrbx + 7, 3, "\x48\x85\xdb", 3);
- if (!testrbx)
- return NULL;
- /* Compute address of the fcwd_access_t ** pointer. */
- return (fcwd_access_t **) (testrbx + peek32 (movrbx + 3));
-}
+#ifdef __x86_64__
+fcwd_access_t **
+find_fast_cwd_pointer_x86_64 ();
+#endif
static fcwd_access_t **
find_fast_cwd ()
@@ -4621,7 +4509,7 @@ find_fast_cwd ()
/* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
we have to make sure we know the version of the FAST_CWD structure
used on the system. */
- f_cwd_ptr = find_fast_cwd_pointer ();
+ f_cwd_ptr = find_fast_cwd_pointer_x86_64 ();
if (!f_cwd_ptr)
small_printf ("Cygwin WARNING:\n"
" Couldn't compute FAST_CWD pointer. This typically occurs if you're using\n"
diff --git a/winsup/cygwin/x86_64/fastcwd.cc b/winsup/cygwin/x86_64/fastcwd.cc
new file mode 100644
index 0000000..6bb8c22
--- /dev/null
+++ b/winsup/cygwin/x86_64/fastcwd.cc
@@ -0,0 +1,128 @@
+/* x86_64/fastcwd.cc: find fast cwd pointer on x86_64 hosts.
+
+ This file is part of Cygwin.
+
+ This software is a copyrighted work licensed under the terms of the
+ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+ details. */
+
+#include "winsup.h"
+
+class fcwd_access_t;
+
+#define peek32(x) (*(int32_t *)(x))
+
+/* This function scans the code in ntdll.dll to find the address of the
+ global variable used to access the CWD. While the pointer is global,
+ it's not exported from the DLL, unfortunately. Therefore we have to
+ use some knowledge to figure out the address. */
+
+fcwd_access_t **
+find_fast_cwd_pointer_x86_64 ()
+{
+ /* Fetch entry points of relevant functions in ntdll.dll. */
+ HMODULE ntdll = GetModuleHandle ("ntdll.dll");
+ if (!ntdll)
+ return NULL;
+ const uint8_t *get_dir = (const uint8_t *)
+ GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
+ const uint8_t *ent_crit = (const uint8_t *)
+ GetProcAddress (ntdll, "RtlEnterCriticalSection");
+ if (!get_dir || !ent_crit)
+ return NULL;
+ /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
+ const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 80);
+ if (!rcall)
+ return NULL;
+ /* Fetch offset from instruction and compute address of called function.
+ This function actually fetches the current FAST_CWD instance and
+ performs some other actions, not important to us. */
+ const uint8_t *use_cwd = rcall + 5 + peek32 (rcall + 1);
+ /* Next we search for the locking mechanism and perform a sanity check.
+ On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
+ Windows 8 does not call RtlEnterCriticalSection. The code manipulates
+ the FastPebLock manually, probably because RtlEnterCriticalSection has
+ been converted to an inline function. Either way, we test if the code
+ uses the FastPebLock. */
+ const uint8_t *movrbx;
+ const uint8_t *lock = (const uint8_t *)
+ memmem ((const char *) use_cwd, 80,
+ "\xf0\x0f\xba\x35", 4);
+ if (lock)
+ {
+ /* The lock instruction tweaks the LockCount member, which is not at
+ the start of the PRTL_CRITICAL_SECTION structure. So we have to
+ subtract the offset of LockCount to get the real address. */
+ PRTL_CRITICAL_SECTION lockaddr =
+ (PRTL_CRITICAL_SECTION) (lock + 9 + peek32 (lock + 4)
+ - offsetof (RTL_CRITICAL_SECTION, LockCount));
+ /* Test if lock address is FastPebLock. */
+ if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
+ return NULL;
+ /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
+ address of the current fcwd_access_t pointer, and it should be pretty
+ near to the locking stuff. */
+ movrbx = (const uint8_t *) memmem ((const char *) lock, 40,
+ "\x48\x8b\x1d", 3);
+ }
+ else
+ {
+ /* Usually the callq RtlEnterCriticalSection follows right after
+ fetching the lock address. */
+ int call_rtl_offset = 7;
+ /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
+ %rcx for the subsequent RtlEnterCriticalSection call. */
+ lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
+ "\x48\x8d\x0d", 3);
+ if (!lock)
+ {
+ /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
+ ops, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
+ lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
+ "\x4c\x8d\x25", 3);
+ call_rtl_offset = 14;
+ }
+
+ if (!lock)
+ {
+ /* A recent Windows 11 Preview calls `lea rel(rip),%r13' then
+ some unrelated instructions, then `callq RtlEnterCriticalSection'.
+ */
+ lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
+ "\x4c\x8d\x2d", 3);
+ call_rtl_offset = 24;
+ }
+
+ if (!lock)
+ {
+ return NULL;
+ }
+
+ PRTL_CRITICAL_SECTION lockaddr =
+ (PRTL_CRITICAL_SECTION) (lock + 7 + peek32 (lock + 3));
+ /* Test if lock address is FastPebLock. */
+ if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
+ return NULL;
+ /* Next is the `callq RtlEnterCriticalSection'. */
+ lock += call_rtl_offset;
+ if (lock[0] != 0xe8)
+ return NULL;
+ const uint8_t *call_addr = (const uint8_t *)
+ (lock + 5 + peek32 (lock + 1));
+ if (call_addr != ent_crit)
+ return NULL;
+ /* In contrast to the above Windows 8 code, we don't have to search
+ for the `mov rel(%rip),%rbx' instruction. It follows right after
+ the call to RtlEnterCriticalSection. */
+ movrbx = lock + 5;
+ }
+ if (!movrbx)
+ return NULL;
+ /* Check that the next instruction tests if the fetched value is NULL. */
+ const uint8_t *testrbx = (const uint8_t *)
+ memmem (movrbx + 7, 3, "\x48\x85\xdb", 3);
+ if (!testrbx)
+ return NULL;
+ /* Compute address of the fcwd_access_t ** pointer. */
+ return (fcwd_access_t **) (testrbx + peek32 (movrbx + 3));
+}