| /* cygload.cc |
| |
| Written by Max Kaehn <slothman@electric-cloud.com> |
| |
| This software is a copyrighted work licensed under the terms of the |
| Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. |
| |
| Note that dynamically linking to cygwin1.dll automatically places your code |
| under the GPL unless you purchase a Cygwin Contract with Red Hat, Inc. |
| See http://www.redhat.com/software/cygwin/ for more information. |
| |
| cygload demonstrates how to dynamically load cygwin1.dll. The default |
| build uses MinGW to compile it; the Makefile also shows how to build |
| it using the Microsoft compiler. |
| |
| By default, the program will silently test basic functionality: |
| * Making space on the stack for cygtls |
| * Loading and initializing cygwin1.dll |
| * Path translation |
| * Error handling |
| * Signal handling |
| |
| Options for this program: |
| -v Verbose output. Normal operation is entirely silent, |
| save for errors. |
| -testinterrupts Pauses the program for 30 seconds so you can demonstrate |
| that it handles ^C properly. |
| -cygwin Name of DLL to load. Defaults to "cygwin1.dll". */ |
| |
| #include "cygload.h" |
| #include <iostream> |
| #include <sstream> |
| #include <vector> |
| #include <errno.h> // for ENOENT |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| using std::cout; |
| using std::cerr; |
| using std::endl; |
| using std::string; |
| |
| cygwin::padding *cygwin::padding::_main = NULL; |
| DWORD cygwin::padding::_mainTID = 0; |
| |
| // A few cygwin constants. |
| static const int SIGHUP = 1; |
| static const int SIGINT = 2; |
| static const int SIGTERM = 15; // Cygwin won't deliver this one to us; |
| // expect unadorned "kill" to just kill |
| // your process. |
| static const int SIGSTOP = 17; // Cygwin insists on delivering SIGSTOP to |
| // the main thread. If your main thread |
| // is not interruptible, you'll miss the |
| // signal and ignore the request to suspend. |
| static const int SIGTSTP = 18; // ^Z on a tty. |
| static const int SIGCONT = 19; // Resume a stopped process. |
| static const int SIGUSR1 = 30; |
| static const int SIGUSR2 = 31; |
| |
| // Using *out instead of cout. In verbose mode, out == &cout. |
| static std::ostream *out = NULL; |
| |
| cygwin::padding::padding () |
| { |
| _main = this; |
| _mainTID = GetCurrentThreadId (); |
| |
| _end = _padding + sizeof (_padding); |
| char *stackbase; |
| #ifdef __GNUC__ |
| __asm__ ( |
| "movl %%fs:4, %0" |
| :"=r"(stackbase) |
| ); |
| #else |
| __asm |
| { |
| mov eax, fs:[4]; |
| mov stackbase, eax; |
| } |
| #endif |
| _stackbase = stackbase; |
| |
| // We've gotten as close as we can to the top of the stack. Even |
| // subverting the entry point, though, still doesn't get us there-- I'm |
| // getting 64 bytes in use before the entry point. So we back up the data |
| // there and restore it when the destructor is called: |
| if ((_stackbase - _end) != 0) |
| { |
| size_t delta = (_stackbase - _end); |
| |
| _backup.resize (delta); |
| |
| memcpy (&(_backup[0]), _end, delta); |
| } |
| } |
| |
| cygwin::padding::~padding () |
| { |
| _main = NULL; |
| |
| if (_backup.size ()) |
| { |
| memcpy (_end, &(_backup[0]), _backup.size ()); |
| } |
| } |
| |
| void |
| cygwin::padding::check () |
| { |
| if (_main == NULL) |
| throw std::runtime_error ("No padding declared!"); |
| if (_mainTID != GetCurrentThreadId ()) |
| throw std::runtime_error ("You need to initialize cygwin::connector " |
| "in the same thread in which you declared the " |
| "padding."); |
| |
| if (_main->_backup.size ()) |
| *out << "Warning! Stack base is " |
| << static_cast<void *>(_main->_stackbase) |
| << ". padding ends at " << static_cast<void *>(_main->_end) |
| << ". Delta is " << (_main->_stackbase - _main->_end) |
| << ". Stack variables could be overwritten!" << endl; |
| } |
| |
| cygwin::connector::connector (const char *dll) |
| { |
| // This will throw if padding is not in place. |
| padding::check (); |
| |
| *out << "Loading " << dll << "..." << endl; |
| |
| // This should call init.cc:dll_entry() with DLL_PROCESS_ATTACH, |
| // which calls dll_crt0_0(). |
| if ((_library = LoadLibrary (dll)) == NULL) |
| throw windows_error ("LoadLibrary", dll); |
| |
| *out << "Initializing cygwin..." << endl; |
| |
| // This calls dcrt0.cc:cygwin_dll_init(), which calls dll_crt0_1(), |
| // which will, among other things: |
| // * spawn the cygwin signal handling thread from sigproc_init() |
| // * initialize the thread-local storage for this thread and overwrite |
| // the first 4K of the stack |
| void (*cyginit) (); |
| get_symbol ("cygwin_dll_init", cyginit); |
| (*cyginit) (); |
| |
| *out << "Loading symbols..." << endl; |
| |
| // Pick up the function pointers for the basic infrastructure. |
| get_symbol ("__errno", _errno); |
| get_symbol ("strerror", _strerror); |
| get_symbol ("cygwin_conv_to_full_posix_path", _conv_to_full_posix_path); |
| get_symbol ("cygwin_conv_to_full_win32_path", _conv_to_full_win32_path); |
| |
| // Note that you need to be running an interruptible cygwin function if |
| // you want to receive signals. You can use the standard signal() |
| // mechanism if you're willing to have your main thread spend all its time |
| // in interruptible cygwin functions like sleep(). Christopher Faylor |
| // cautions that this solution "could be slightly racy": if a second |
| // signal comes in before the first one is done processing, the thread |
| // won't be back in sigwait() to catch it. |
| *out << "Spawning signal handling thread..." << endl; |
| |
| _waiting_for_signals = true; |
| _signal_thread_done = false; |
| InitializeCriticalSection (&_thread_mutex); |
| |
| DWORD tid; |
| |
| _signal_thread = CreateThread (NULL, // Default security. |
| 32768, // Adjust the stack size as |
| // appropriate for what your signal |
| // handler needs in order to run, and |
| // then add 4K for cygtls. |
| &signal_thread, // Function to call |
| this, // Context |
| 0, // Flags |
| &tid); // Thread ID |
| |
| if (_signal_thread == NULL) |
| throw windows_error ("CreateThread", "signal_thread"); |
| } |
| |
| cygwin::connector::~connector () |
| { |
| try |
| { |
| // First, shut down signal handling. |
| int (*raze) (int); |
| int (*pthread_join) (void *, void **); |
| |
| get_symbol ("raise", raze); |
| get_symbol ("pthread_join", pthread_join); |
| |
| // Tell the listener to shut down... |
| _waiting_for_signals = false; |
| int err = 0; |
| EnterCriticalSection (&_thread_mutex); |
| if (!_signal_thread_done) |
| err = raze (SIGUSR2); |
| LeaveCriticalSection (&_thread_mutex); |
| if (err) |
| cerr << error (this, "raise", "SIGUSR2").what () << endl; |
| // ...and get the thread to join. |
| if (!CloseHandle (_signal_thread)) |
| throw windows_error ("CloseHandle", "signal_thread"); |
| |
| // This should call init.cc:dll_entry() with DLL_PROCESS_DETACH. |
| if (!FreeLibrary (_library)) |
| throw windows_error ("FreeLibrary", "cygwin1.dll"); |
| } |
| catch (std::exception &x) |
| { |
| cerr << x.what () << endl; |
| } |
| } |
| |
| DWORD WINAPI |
| cygwin::connector::signal_thread (void *param) |
| { |
| connector *that = reinterpret_cast < connector * > (param); |
| |
| try |
| { |
| that->await_signal (); |
| } |
| catch (std::exception &x) |
| { |
| cerr << "signal_thread caught " << x.what () << endl; |
| return 0; |
| } |
| return 0; |
| } |
| |
| void |
| cygwin::connector::await_signal () |
| { |
| // Wait for signals. |
| unsigned long sigset[32]; |
| int sig; |
| int (*empty) (void *); |
| int (*add) (void *, int); |
| int (*wait) (void *, int *); |
| |
| get_symbol ("sigemptyset", empty); |
| get_symbol ("sigaddset", add); |
| get_symbol ("sigwait", wait); |
| |
| empty (sigset); |
| add (sigset, SIGHUP); |
| add (sigset, SIGINT); |
| // add (sigset, SIGSTOP); |
| // add (sigset, SIGTSTP); // I can't get this to suspend properly, so |
| // I'll leave it up to chance that the main |
| // thread is interruptible. |
| add (sigset, SIGUSR1); |
| add (sigset, SIGUSR2); |
| |
| while (_waiting_for_signals) |
| { |
| int err = wait (sigset, &sig); |
| if (err) |
| cerr << error (this, "sigwait").what () << endl; |
| else |
| *out << "Received signal " << sig << "." << endl; |
| switch (sig) |
| { |
| case SIGUSR2: |
| if (!_waiting_for_signals) |
| { |
| // SIGUSR2 is how ~connector wakes this thread |
| goto done; |
| } |
| break; |
| default: |
| break; |
| } |
| handle_signals (sig); |
| } |
| done: |
| EnterCriticalSection (&_thread_mutex); |
| _signal_thread_done = true; |
| LeaveCriticalSection (&_thread_mutex); |
| |
| *out << "await_signal done." << endl; |
| } |
| |
| cygwin::connector::signal_handler * |
| cygwin::connector::set_handler (int signal, signal_handler *handler) |
| { |
| signal_handler *retval = _signal_handlers[signal]; |
| |
| if (handler == NULL) |
| _signal_handlers.erase (signal); |
| else |
| _signal_handlers[signal] = handler; |
| |
| return retval; |
| } |
| |
| void |
| cygwin::connector::handle_signals (int sig) |
| { |
| callback_list::iterator h = _signal_handlers.find (sig); |
| |
| if (h != _signal_handlers.end ()) |
| { |
| try |
| { |
| signal_handler *handler = h->second; |
| (*handler) (sig); |
| return; |
| } |
| catch (std::exception &x) |
| { |
| cerr << "cygwin::connector::handle_signals caught " |
| << x.what () << "!" << endl; |
| return; |
| } |
| } |
| |
| cerr << "No handler for signal " << sig << "!" << endl; |
| } |
| |
| int |
| cygwin::connector::err_no () const |
| { |
| int *e = (*_errno) (); |
| if (e == NULL) |
| { |
| return -1; |
| } |
| return *e; |
| } |
| |
| string |
| cygwin::connector::str_error (int err_no) const |
| { |
| string retval; |
| |
| const char *s = (*_strerror) (err_no); |
| if (s != NULL) |
| { |
| retval = s; |
| } |
| else |
| { |
| std::ostringstream o; |
| o << "Unexpected errno " << err_no; |
| retval = o.str (); |
| } |
| |
| return retval; |
| } |
| |
| string |
| cygwin::connector::unix_path (const string &windows) const |
| { |
| char buf[MAX_PATH]; |
| |
| _conv_to_full_posix_path (windows.c_str (), buf); |
| |
| return string (buf); |
| } |
| |
| string |
| cygwin::connector::win_path (const string &unix) const |
| { |
| char buf[MAX_PATH]; |
| |
| _conv_to_full_win32_path (unix.c_str (), buf); |
| |
| return string (buf); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| string |
| cygwin::error::format (cygwin::connector *c, |
| int err_no, const char *message, const char *detail) |
| { |
| std::ostringstream ret; |
| |
| ret << message; |
| if (detail) |
| { |
| ret << "(" << detail << ")"; |
| } |
| ret << ": " << c->str_error (err_no); |
| |
| return ret.str (); |
| } |
| |
| string |
| windows_error::format (DWORD error, const char *message, const char *detail) |
| { |
| std::ostringstream ret; |
| char buf[512]; |
| DWORD bytes; |
| |
| ret << message; |
| if (detail) |
| ret << "(" << detail << ")"; |
| ret << ": "; |
| |
| bytes = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 0, error, |
| MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), |
| buf, sizeof (buf), 0); |
| |
| if (bytes == 0) |
| ret << "Unexpected Windows error " << error; |
| else |
| { |
| // Remove trailing whitespace |
| char *p = buf + bytes - 1; |
| while (isspace (*p)) |
| *p-- = '\0'; |
| ret << buf << " (" << error << ")"; |
| } |
| |
| return ret.str (); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| extern "C" int mainCRTStartup (); |
| |
| // This just pushes 4K onto the stack, backs up the original stack, and |
| // jumps into the regular startup code. This avoids having to worry about |
| // backing up argc and argv. |
| extern "C" int __stdcall |
| cygloadCRTStartup () |
| { |
| cygwin::padding padding; |
| return mainCRTStartup (); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static void |
| hangup (int sig) |
| { |
| cout << "Hangup (" << sig << ")." << endl; |
| } |
| |
| static void |
| interrupt (int sig) |
| { |
| cerr << "Interrupt (" << sig << ")!" << endl; |
| exit (0); |
| } |
| |
| static int caught = false; |
| |
| static void |
| catch_signal (int) |
| { |
| *out << "Signals are working." << endl; |
| caught = true; |
| } |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| // If you do not want to use cygloadCRTStartup() as an entry point, |
| // uncomment this line, but be sure to have *everything* you want |
| // from the stack below it backed up before you call the |
| // constructor for cygwin::connector. |
| //cygwin::padding padding; |
| |
| std::ostringstream output; |
| bool verbose = false, testinterrupts = false; |
| const char *dll = "cygwin1.dll"; |
| |
| out = &output; |
| |
| for (int i = 1; i < argc; ++i) |
| { |
| string arg = string (argv[i]); |
| |
| if (arg == "-v") |
| { |
| verbose = true; |
| out = &cout; |
| } |
| else if (arg == "-testinterrupts") |
| testinterrupts = true; |
| else if (arg == "-cygwin") |
| { |
| if (i+1 >= argc) |
| { |
| cerr << "Need to supply an argument with -cygwin." << endl; |
| return 255; |
| } |
| dll = argv[++i]; |
| } |
| } |
| |
| |
| try |
| { |
| *out << "Connecting to cygwin..." << endl; |
| cygwin::connector cygwin (dll); |
| *out << "Successfully connected." << endl; |
| |
| string result = cygwin.str_error (ENOENT); |
| |
| if (result != "No such file or directory") |
| { |
| cerr << "strerror(ENOENT) returned \"" |
| << result |
| << "\" instead of \"No such file or directory\"!" |
| << endl; |
| return 1; |
| } |
| else if (verbose) |
| { |
| *out << "strerror(ENOENT) = " << result << endl; |
| } |
| |
| // Path conversion: from cygwin to Windows... |
| result = cygwin.win_path ("/usr"); |
| struct _stat statbuf; |
| if (::_stat (result.c_str (), &statbuf) < 0) |
| { |
| cerr << "stat(\"" << result << "\") failed!" << endl; |
| return 2; |
| } |
| else if (verbose) |
| { |
| *out << "/usr == " << result << endl; |
| } |
| |
| // ...and back: |
| char buf[MAX_PATH], scratch[256]; |
| GetSystemDirectory (buf, sizeof(buf)); |
| int (*cygstat) (const char *, void *); |
| cygwin.get_symbol ("stat", cygstat); |
| |
| if (cygstat (buf, scratch) < 0) |
| { |
| cerr << "cygwin stat(\"" << buf << "\") failed!" << endl; |
| return 3; |
| } |
| else if (verbose) |
| { |
| *out << buf << " == " << cygwin.unix_path(buf) << endl; |
| } |
| |
| // Test error handling. This should output |
| // "open(/potrzebie/furshlugginer): No such file or directory" |
| { |
| int (*cygopen) (const char *, int); |
| cygwin.get_symbol ("open", cygopen); |
| |
| if (cygopen ("/potrzebie/furshlugginer", 0 /* O_RDONLY */ ) < 0) |
| { |
| int err = cygwin.err_no (); |
| if (err != ENOENT) |
| { |
| cerr << "cygwin open(\"/potrzebie/furshlugginer\", " |
| "O_RDONLY): expected to fail with ENOENT, got " |
| << err << "!" << endl; |
| return 4; |
| } |
| if (verbose) |
| *out << cygwin::error (&cygwin, "open", |
| "/potrzebie/furshlugginer").what () |
| << endl; |
| } |
| else |
| { |
| cerr << "/potrzebie/furshlugginer should not exist!" |
| << endl; |
| return 5; |
| } |
| } |
| |
| // And signal handling: |
| std::pointer_to_unary_function < int , void > h1 (&hangup); |
| std::pointer_to_unary_function < int , void > h2 (&interrupt); |
| std::pointer_to_unary_function < int , void > h3 (&catch_signal); |
| cygwin.set_handler (SIGHUP, &h1); |
| cygwin.set_handler (SIGINT, &h2); |
| cygwin.set_handler (SIGUSR1, &h3); |
| |
| // Make sure the signal handler thread has had time to start... |
| Sleep (100); |
| // Send a test signal to set "caught" to true... |
| int (*raze) (int); |
| cygwin.get_symbol ("raise", raze); |
| raze (SIGUSR1); |
| // And give the thread time to wait for the shutdown signal. |
| Sleep (100); |
| |
| if (testinterrupts) |
| { |
| // This is a worst case scenario for testing interrupts: the |
| // main thread is in a long-duration Windows API call. This |
| // makes the main thread uninterruptible; cygwin will retry |
| // 20 times, with a low_priority_sleep(0) between each try. |
| cout << "Sleeping for 30 seconds, waiting for a signal..." << endl; |
| Sleep (30000); |
| cout << "Done waiting." << endl; |
| } |
| } |
| catch (std::exception &x) |
| { |
| cerr << x.what () << endl; |
| return 2; |
| } |
| |
| if (caught) |
| return 0; |
| else |
| { |
| cerr << "Never received SIGUSR1." << endl; |
| return 1; |
| } |
| } |