From 833db5481f1f86745979d3ceda580c2d8fdd24a8 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Tue, 16 Aug 2011 20:08:34 +0000 Subject: [PATCH] * dlfcn.cc (dlopen): Reimplement RTLD_NODELETE for Windows 2000 using internal datastructures. Explain the code. * ntdll.h (struct _LDR_DATA_TABLE_ENTRY): Define. (struct _PEB_LDR_DATA): Define. (struct _PEB): Change PVOID LoaderData to PPEB_LDR_DATA Ldr. * fhandler_process.cc (format_process_maps): Call NtQueryVirtualMemory with valid return length pointer. Explain why. --- winsup/cygwin/ChangeLog | 11 +++++++++++ winsup/cygwin/dlfcn.cc | 27 +++++++++++++++++++++------ winsup/cygwin/fhandler_process.cc | 8 ++++++-- winsup/cygwin/ntdll.h | 30 +++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 452d17c4d..315972e58 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,14 @@ +2011-08-16 Corinna Vinschen + + * dlfcn.cc (dlopen): Reimplement RTLD_NODELETE for Windows 2000 using + internal datastructures. Explain the code. + * ntdll.h (struct _LDR_DATA_TABLE_ENTRY): Define. + (struct _PEB_LDR_DATA): Define. + (struct _PEB): Change PVOID LoaderData to PPEB_LDR_DATA Ldr. + + * fhandler_process.cc (format_process_maps): Call NtQueryVirtualMemory + with valid return length pointer. Explain why. + 2011-08-16 Corinna Vinschen * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc index 91ffc9a6c..fbcdfedfb 100644 --- a/winsup/cygwin/dlfcn.cc +++ b/winsup/cygwin/dlfcn.cc @@ -120,12 +120,27 @@ dlopen (const char *name, int flags) (HMODULE *) &ret)) { /* Windows 2000 is missing the GetModuleHandleEx call, so we - just use a trick. Call LoadLibrary 10 times more if the - RTLD_NODELETE flag has been specified. That makes it - unlikely (but not impossible) that dlclose will actually - free the library. */ - for (int i = 0; i < 10; ++i) - LoadLibraryW (path); + use a non-documented way to set the DLL to "don't free". + This is how it works: Fetch the Windows Loader data from + the PEB. Iterate backwards through the list of loaded + DLLs and compare the DllBase address with the address + returned by LoadLibrary. If they are equal we found the + right entry. Now set the LoadCount to -1, which is the + marker for a DLL which should never be free'd. */ + PPEB_LDR_DATA ldr = NtCurrentTeb ()->Peb->Ldr; + + for (PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY) + ldr->InLoadOrderModuleList.Blink; + entry && entry->DllBase; + entry = (PLDR_DATA_TABLE_ENTRY) + entry->InLoadOrderLinks.Blink) + { + if (entry->DllBase == ret) + { + entry->LoadCount = (WORD) -1; + break; + } + } } } diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc index eb36d9d56..36270d1ba 100644 --- a/winsup/cygwin/fhandler_process.cc +++ b/winsup/cygwin/fhandler_process.cc @@ -960,12 +960,16 @@ format_process_maps (void *data, char *&destbuf) // if a new allocation, figure out what kind it is if (newbase && !last_pass && mb.State != MEM_FREE) { + /* If the return length pointer is missing, NtQueryVirtualMemory + returns with STATUS_ACCESS_VIOLATION on Windows 2000. */ + ULONG ret_len = 0; + st.st_dev = 0; st.st_ino = 0; if ((mb.Type & (MEM_MAPPED | MEM_IMAGE)) - && NT_SUCCESS (NtQueryVirtualMemory (proc, cur.abase, + && NT_SUCCESS (status = NtQueryVirtualMemory (proc, cur.abase, MemorySectionName, - msi, 65536, NULL))) + msi, 65536, &ret_len))) { PWCHAR dosname = drive_maps.fixup_if_match (msi->SectionFileName.Buffer); diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index c5b3597a7..ce1a87a34 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -579,6 +579,34 @@ typedef struct _KERNEL_USER_TIMES LARGE_INTEGER UserTime; } KERNEL_USER_TIMES, *PKERNEL_USER_TIMES; +typedef struct _LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + WORD LoadCount; + /* More follows. Left out since it's just not used. The aforementioned + part of the structure is stable from at least NT4 up to Windows 7, + including WOW64. */ +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; + +typedef struct _PEB_LDR_DATA +{ + ULONG Length; + UCHAR Initialized; + PVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + typedef struct _RTL_USER_PROCESS_PARAMETERS { ULONG AllocationSize; @@ -616,7 +644,7 @@ typedef struct _PEB BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[9]; - PVOID LoaderData; + PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; BYTE Reserved3[4]; PVOID ProcessHeap;