diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 4ff4cc971..1e6ea30e5 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,25 @@ +2005-03-22 Christopher Faylor + + * Makefile.in (DLL_OFILES): Add hookapi.o. Eliminate some cruft. + * cygheap.h (cygheap_types): Add new enum: HEAP_1_HOOK. + (hook_chain): New struct. + (init_cygheap::hooks): Define new element. + * cygheap.cc (cygheap_fixup_in_child): Zero hook chain on exec. + * dcrt0.cc (dll_crt0_1): Call ld_preload just before calling main function. + * external.cc (cygwin_internal): Implement CW_HOOK. + * fork.cc (fork_child): Call fixup_hooks_after_fork. + * init.cc (cygwin_hmodule): Reinstate after a long absence. + * include/sys/cygwin.h: Define CW_HOOK. + * hookapi.cc: New file. + + * select.cc (start_thread_socket): Add debugging output. + + * fhandler_disk_file.cc (fhandler_disk_file::fchmod): gcc 4.x + accommodation. + + * fhandler_socket.cc (fhandler_socket::connect): Make sure that err is + initialized. + 2005-03-22 Corinna Vinschen * cygwin.din (__ctype_ptr): Export. diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index de9c9f8bb..589da2b28 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -117,7 +117,7 @@ MALLOC_OFILES=@MALLOC_OFILES@ DLL_IMPORTS:=$(w32api_lib)/libkernel32.a $(w32api_lib)/libadvapi32.a MT_SAFE_OBJECTS:= -# Please maintain this list in sorted order, with maximum files per 80 col line +# Please maintain this list in sorted order, with maximum files per 85 col line # DLL_OFILES:=assert.o autoload.o bsdlib.o cxx.o cygheap.o cygthread.o cygtls.o \ dcrt0.o debug.o delqueue.o devices.o dir.o dlfcn.o dll_init.o dtable.o \ @@ -127,12 +127,12 @@ DLL_OFILES:=assert.o autoload.o bsdlib.o cxx.o cygheap.o cygthread.o cygtls.o \ fhandler_nodevice.o fhandler_proc.o fhandler_process.o \ fhandler_random.o fhandler_raw.o fhandler_registry.o fhandler_serial.o \ fhandler_socket.o fhandler_tape.o fhandler_termios.o \ - fhandler_tty.o fhandler_virtual.o fhandler_windows.o \ - fhandler_zero.o flock.o fnmatch.o fork.o getopt.o glob.o grp.o heap.o \ + fhandler_tty.o fhandler_virtual.o fhandler_windows.o fhandler_zero.o \ + flock.o fnmatch.o fork.o getopt.o glob.o grp.o heap.o hookapi.o \ init.o ioctl.o ipc.o iruserok.o localtime.o malloc_wrapper.o miscfuncs.o \ mmap.o msg.o net.o netdb.o ntea.o passwd.o path.o pinfo.o pipe.o \ - poll.o pthread.o regcomp.o regerror.o regexec.o regfree.o registry.o \ - resource.o scandir.o sched.o sec_acl.o sec_helper.o security.o \ + poll.o pthread.o regcomp.o regerror.o regexec.o regfree.o \ + registry.o resource.o scandir.o sched.o sec_acl.o sec_helper.o security.o \ select.o sem.o shared.o shm.o sigfe.o signal.o sigproc.o smallprint.o \ spawn.o strace.o strsep.o strsig.o sync.o syscalls.o sysconf.o \ syslog.o termios.o thread.o timer.o times.o tty.o uinfo.o uname.o \ @@ -254,6 +254,7 @@ grp_CFLAGS:=-fomit-frame-pointer malloc_CFLAGS:=-fomit-frame-pointer malloc_wrapper_CFLAGS:=-fomit-frame-pointer miscfuncs_CFLAGS:=-fomit-frame-pointer +net_CFLAGS:=-fomit-frame-pointer passwd_CFLAGS:=-fomit-frame-pointer regcomp_CFLAGS=-fomit-frame-pointer regerror_CFLAGS=-fomit-frame-pointer @@ -267,7 +268,7 @@ uinfo_CFLAGS:=-fomit-frame-pointer endif .PHONY: all force dll_ofiles install all_target install_target all_host install_host \ - install install-libs install-headers -lgcc + install install-libs install-headers .SUFFIXES: .SUFFIXES: .c .cc .def .a .o .d .s @@ -434,11 +435,6 @@ winver_stamp: mkvers.sh include/cygwin/version.h winver.rc $(DLL_OFILES) $(COMPILE_CXX) -o version.o version.cc && \ touch $@ --lgcc: - : - -# - Makefile: cygwin.din $(DEF_FILE): gendef cygwin.din $(srcdir)/tlsoffsets.h diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc index a8bf8e410..ef5804d6d 100644 --- a/winsup/cygwin/cygheap.cc +++ b/winsup/cygwin/cygheap.cc @@ -21,7 +21,6 @@ #include "child_info.h" #include "heap.h" #include "sync.h" -#include "shared_info.h" #include "sigproc.h" init_cygheap NO_COPY *cygheap; @@ -185,6 +184,7 @@ cygheap_fixup_in_child (bool execed) if (execed) { + cygheap->hooks.next = NULL; cygheap->user_heap.base = NULL; /* We can allocate the heap anywhere */ /* Walk the allocated memory chain looking for orphaned memory from previous execs */ diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index 9a57a9b2d..a338c6ed7 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -21,6 +21,7 @@ enum cygheap_types HEAP_ARCHETYPES, HEAP_TLS, HEAP_1_START, + HEAP_1_HOOK, HEAP_1_STR, HEAP_1_ARGV, HEAP_1_BUF, @@ -262,6 +263,13 @@ struct user_heap_info unsigned chunk; }; +struct hook_chain +{ + void **loc; + const void *func; + struct hook_chain *next; +}; + struct init_cygheap { _cmalloc_entry *chain; @@ -291,6 +299,7 @@ struct init_cygheap int open_fhs; pid_t pid; /* my pid */ HANDLE pid_handle; /* handle for my pid */ + hook_chain hooks; void close_ctty (); }; diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index 524b30034..fdec9bc41 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -41,6 +41,8 @@ details. */ #define PREMAIN_LEN (sizeof (user_data->premain) / sizeof (user_data->premain[0])) +void ld_preload (); + HANDLE NO_COPY hMainProc = (HANDLE) -1; HANDLE NO_COPY hMainThread; @@ -857,6 +859,7 @@ dll_crt0_1 (char *) do this for noncygwin case since the signal thread is blocked due to LoadLibrary serialization. */ wait_for_sigthread (); + ld_preload (); if (user_data->main) exit (user_data->main (__argc, __argv, *user_data->envptr)); } diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc index 56ef0c6b6..c28039cc0 100644 --- a/winsup/cygwin/external.cc +++ b/winsup/cygwin/external.cc @@ -28,6 +28,8 @@ details. */ #include "pwdgrp.h" #include "cygtls.h" +void *hook_cygwin (const char *, const void *); + static external_pinfo * fillout_pinfo (pid_t pid, int winpid) { @@ -298,6 +300,12 @@ cygwin_internal (cygwin_getinfo_types t, ...) } return p.binmode (); } + case CW_HOOK: + { + const char *name = va_arg (arg, const char *); + const void *hookfn = va_arg (arg, const void *); + return (unsigned long) hook_cygwin (name, hookfn); + } default: return (DWORD) -1; } diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index 7842bcafa..da89e5422 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -429,9 +429,9 @@ fhandler_disk_file::fchmod (mode_t mode) /* if the mode we want has any write bits set, we can't be read only. */ if (mode & (S_IWUSR | S_IWGRP | S_IWOTH)) - (DWORD) pc &= ~FILE_ATTRIBUTE_READONLY; + pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY; else - (DWORD) pc |= FILE_ATTRIBUTE_READONLY; + pc |= (DWORD) FILE_ATTRIBUTE_READONLY; if (!SetFileAttributes (pc, pc)) __seterrno (); diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index c2e87478d..e7194dfe5 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -698,7 +698,9 @@ fhandler_socket::connect (const struct sockaddr *name, int namelen) res = ::connect (get_socket (), (sockaddr *) &sin, namelen); - if (res) + if (!res) + err = 0; + else { err = WSAGetLastError (); /* Special handling for connect to return the correct error code diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index b04e518fe..4ae2d0a21 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -154,6 +154,7 @@ sync_with_parent (const char *s, bool hang_self) static int __stdcall fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls) { + extern void fixup_hooks_after_fork (); extern void fixup_timers_after_fork (); debug_printf ("child is running. pid %d, ppid %d, stack here %p", myself->pid, myself->ppid, __builtin_frame_address (0)); @@ -232,6 +233,7 @@ fork_child (HANDLE& hParent, dll *&first_dll, bool& load_dlls) pthread::atforkchild (); fixup_timers_after_fork (); + fixup_hooks_after_fork (); cygbench ("fork-child"); cygwin_finished_initializing = true; return 0; diff --git a/winsup/cygwin/hookapi.cc b/winsup/cygwin/hookapi.cc new file mode 100644 index 000000000..09c70c24b --- /dev/null +++ b/winsup/cygwin/hookapi.cc @@ -0,0 +1,218 @@ +#include "winsup.h" +#include "cygerrno.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include +#include +#include + +#define rva(coerce, base, addr) (coerce) ((char *) (base) + (addr)) +#define rvacyg(coerce, addr) rva (coerce, cygwin_hmodule, addr) + +struct function_hook +{ + const char *name; // Function name, e.g. "DirectDrawCreateEx". + const void *hookfn; // Address of your function. + void *origfn; // Stored by HookAPICalls, the address of the original function. +}; + +/* Given an HMODULE, returns a pointer to the PE header */ +static PIMAGE_NT_HEADERS +PEHeaderFromHModule (HMODULE hModule) +{ + PIMAGE_NT_HEADERS pNTHeader = NULL; + + if (PIMAGE_DOS_HEADER(hModule) ->e_magic != IMAGE_DOS_SIGNATURE) + /* nothing */; + else + { + pNTHeader = PIMAGE_NT_HEADERS (PBYTE (hModule) + + PIMAGE_DOS_HEADER (hModule) ->e_lfanew); + if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) + pNTHeader = NULL; + } + + return pNTHeader; +} + +void * +putmem (PIMAGE_THUNK_DATA pi, const void *hookfn) +{ + + DWORD flOldProtect, flNewProtect, flDontCare; + MEMORY_BASIC_INFORMATION mbi; + + /* Get the current protection attributes */ + VirtualQuery (pi, &mbi, sizeof (mbi)); + + /* Remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag */ + flNewProtect = mbi.Protect; + flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); + flNewProtect |= PAGE_READWRITE; + + if (!VirtualProtect (pi, sizeof (PVOID), flNewProtect, &flOldProtect) ) + return NULL; + + void *origfn = (void *) pi->u1.Function; + pi->u1.Function = (DWORD) hookfn; + + (void) VirtualProtect (pi, sizeof (PVOID), flOldProtect, &flDontCare); + return origfn; +} + +/* Builds stubs for and redirects the IAT for one DLL (pImportDesc) */ + +static bool +RedirectIAT (function_hook& fh, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, + HMODULE hm) +{ + // If no import names table, we can't redirect this, so bail + if (pImportDesc->OriginalFirstThunk == 0) + return false; + + /* import address table */ + PIMAGE_THUNK_DATA pt = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->FirstThunk); + /* import names table */ + PIMAGE_THUNK_DATA pn = rva (PIMAGE_THUNK_DATA, hm, pImportDesc->OriginalFirstThunk); + + /* Scan through the IAT, completing the stubs and redirecting the IAT + entries to point to the stubs. */ + for (PIMAGE_THUNK_DATA pi = pt; pn->u1.Ordinal; pi++, pn++) + { + if (IMAGE_SNAP_BY_ORDINAL (pn->u1.Ordinal) ) + continue; + + /* import by name */ + PIMAGE_IMPORT_BY_NAME pimp = rva (PIMAGE_IMPORT_BY_NAME, hm, pn->u1.AddressOfData); + + if (strcmp (fh.name, (char *) pimp->Name) == 0) + { + fh.origfn = putmem (pi, fh.hookfn); + if (!fh.origfn) + return false; + hook_chain *hc; + for (hc = &cygheap->hooks; hc->next; hc = hc->next) + continue; + hc->next = (hook_chain *) cmalloc (HEAP_1_HOOK, sizeof (hook_chain)); + hc->next->loc = (void **) pi; + hc->next->func = fh.hookfn; + hc->next->next = NULL; + break; + } + } + + return true; +} + +void +get_export (function_hook& fh) +{ + extern HMODULE cygwin_hmodule; + PIMAGE_DOS_HEADER pdh = (PIMAGE_DOS_HEADER) cygwin_hmodule; + if (pdh->e_magic != IMAGE_DOS_SIGNATURE) + return; + PIMAGE_NT_HEADERS pnt = (PIMAGE_NT_HEADERS) ((char *) pdh + pdh->e_lfanew); + if (pnt->Signature != IMAGE_NT_SIGNATURE || pnt->FileHeader.SizeOfOptionalHeader == 0) + return; + PIMAGE_EXPORT_DIRECTORY pexp = + rvacyg (PIMAGE_EXPORT_DIRECTORY, + pnt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + if (!pexp) + return; + + PDWORD pfuncs = rvacyg (PDWORD, pexp->AddressOfFunctions); + PDWORD pnames = rvacyg (PDWORD, pexp->AddressOfNames); + for (DWORD i = 0; i < pexp->NumberOfNames; i++) + if (strcmp (fh.name, rvacyg (char *, pnames[i])) == 0) + { + fh.origfn = rvacyg (void *, pfuncs[i]); + break; + } +} + +static const char * +makename (const char *name, char *&buf, int& i, int inc) +{ + i += inc; + static const char *testers[] = {"NOTUSED", "64", "32"}; + if (i < 0 || i >= (int) (sizeof (testers) / sizeof (testers[0]))) + return NULL; + if (i) + { + __small_sprintf (buf, "_%s%s", name, testers[i]); + name = buf; + } + return name; +} + +// Top level routine to find the EXE's imports, and redirect them +void * +hook_cygwin (const char *name, const void *fn) +{ + HMODULE hm = GetModuleHandle (NULL); + PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule (hm); + + if (!pExeNTHdr) + return false; + + DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory + [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + if (!importRVA) + return false; + + // Convert imports RVA to a usable pointer + PIMAGE_IMPORT_DESCRIPTOR pdfirst = rva (PIMAGE_IMPORT_DESCRIPTOR, hm, importRVA); + + function_hook fh; + fh.origfn = NULL; + fh.hookfn = fn; + char *buf = (char *) alloca (strlen (name) + strlen ("64") + sizeof ("_")); + int i = -1; + while (!fh.origfn && (fh.name = makename (name, buf, i, 1))) + { + // Iterate through each import descriptor, and redirect if appropriate + for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++) + { + PSTR modname = rva (PSTR, hm, pd->Name); + if (strcasematch (modname, "cygwin1.dll")) + RedirectIAT (fh, pd, hm); + } + } + + while (!fh.origfn && (fh.name = makename (name, buf, i, -1))) + get_export (fh); + + return fh.origfn; +} + +void +ld_preload () +{ + char *p = getenv ("LD_PRELOAD"); + if (!p) + return; + char *s = (char *) alloca (strlen (p) + 1); + strcpy (s, p); + char *here = NULL; + for (p = strtok_r (s, " \t\n", &here); p; p = strtok_r (NULL, " \t\n", &here)) + { + path_conv lib (p); + if (!LoadLibrary (lib)) + { + __seterrno (); + api_fatal ("error while loading shared libraries: %s: " + "cannot open shared object file: %s", p, + strerror (get_errno ())); + } + } +} + +void +fixup_hooks_after_fork () +{ + for (hook_chain *hc = &cygheap->hooks; (hc = hc->next); ) + putmem ((PIMAGE_THUNK_DATA) hc->loc, hc->func); +} diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index 04d0b003f..eb29cceb2 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -49,6 +49,7 @@ struct __cygwin_perfile /* External interface stuff */ +/* Always add at the bottom. Do not add new values in the middle. */ typedef enum { CW_LOCK_PINFO, @@ -78,7 +79,8 @@ typedef enum CW_GET_SHMLBA, CW_GET_UID_FROM_SID, CW_GET_GID_FROM_SID, - CW_GET_BINMODE + CW_GET_BINMODE, + CW_HOOK } cygwin_getinfo_types; #define CW_NEXTPID 0x80000000 /* or with pid to get next one */ diff --git a/winsup/cygwin/init.cc b/winsup/cygwin/init.cc index d3e41b6a4..3d7a91c50 100644 --- a/winsup/cygwin/init.cc +++ b/winsup/cygwin/init.cc @@ -108,6 +108,8 @@ respawn_wow64_process () extern void __stdcall dll_crt0_0 (); +HMODULE NO_COPY cygwin_hmodule; + extern "C" int WINAPI dll_entry (HANDLE h, DWORD reason, void *static_load) { @@ -116,6 +118,7 @@ dll_entry (HANDLE h, DWORD reason, void *static_load) switch (reason) { case DLL_PROCESS_ATTACH: + cygwin_hmodule = (HMODULE) h; /* Is the stack at an unusual high address? Check if we're running on a 64 bit machine. If so, respawn. */ if (&is_64bit_machine >= (PBOOL) 0x400000 diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 50489aa1d..8b77d2a70 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -1394,6 +1394,7 @@ start_thread_socket (select_record *me, select_stuff *stuff) SetHandleInformation ((HANDLE) si->exitsock, HANDLE_FLAG_INHERIT, 0); /* else too bad? */ + select_printf ("opened new socket %p", _my_tls.locals.exitsock); } select_printf ("exitsock %p", si->exitsock);