* Makefile.in: Test cygload.
* cygload: New directory. * cygload/README: New file. * cygload/Makefile: Ditto. * cygload/cygload.h: Ditto. * cygload/cygload.cc: Ditto. * cygload/cygload.exp: Ditto.
This commit is contained in:
parent
405e757cf9
commit
bd3b6ab4ee
|
@ -1,3 +1,13 @@
|
||||||
|
2005-06-06 Max Kaehn <slothman@electric-cloud.com>
|
||||||
|
|
||||||
|
* Makefile.in: Test cygload.
|
||||||
|
* cygload: New directory.
|
||||||
|
* cygload/README: New file.
|
||||||
|
* cygload/Makefile: Ditto.
|
||||||
|
* cygload/cygload.h: Ditto.
|
||||||
|
* cygload/cygload.cc: Ditto.
|
||||||
|
* cygload/cygload.exp: Ditto.
|
||||||
|
|
||||||
2005-05-17 Brian Dessent <brian@dessent.net>
|
2005-05-17 Brian Dessent <brian@dessent.net>
|
||||||
|
|
||||||
* winsup.api/signal-into-win32-api.c (main): Use 'NULL' instead of '0'
|
* winsup.api/signal-into-win32-api.c (main): Use 'NULL' instead of '0'
|
||||||
|
|
|
@ -186,7 +186,8 @@ check: $(TESTSUP_LIB_NAME) $(RUNTIME) cygrun.exe testsuite/site.exp
|
||||||
TCL_LIBRARY=`cd .. ; cd ${srcdir}/../../tcl/library ; pwd` ; \
|
TCL_LIBRARY=`cd .. ; cd ${srcdir}/../../tcl/library ; pwd` ; \
|
||||||
export TCL_LIBRARY ; fi ; \
|
export TCL_LIBRARY ; fi ; \
|
||||||
PATH=$(bupdir)/cygwin:$${PATH} ;\
|
PATH=$(bupdir)/cygwin:$${PATH} ;\
|
||||||
$(RUNTEST) --tool winsup $(RUNTESTFLAGS)
|
$(RUNTEST) --tool winsup $(RUNTESTFLAGS) ;\
|
||||||
|
$(RUNTEST) --tool cygload $(RUNTESTFLAGS)
|
||||||
|
|
||||||
cygrun.o: cygrun.c
|
cygrun.o: cygrun.c
|
||||||
$(CC) $(MINGW_CFLAGS) -o $@ -c $<
|
$(CC) $(MINGW_CFLAGS) -o $@ -c $<
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Makefile for cygload
|
||||||
|
|
||||||
|
###
|
||||||
|
### MinGW options
|
||||||
|
###
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -mno-cygwin -Wall
|
||||||
|
LINKFLAGS = -lstdc++ -Wl,-e,_cygloadCRTStartup@0
|
||||||
|
|
||||||
|
###
|
||||||
|
### MSVC options
|
||||||
|
###
|
||||||
|
ifndef MSVCDir
|
||||||
|
MSVCDir = C:/cygwin/usr/local/tools/i686_win32/vc7/Vc7
|
||||||
|
endif
|
||||||
|
|
||||||
|
CL = $(MSVCDir)/bin/cl
|
||||||
|
# If you want to look at the assembly, add "/Famsvc-cygload.asm /FAs".
|
||||||
|
MSVCCFLAGS = /nologo /GX /MDd /Zi /W4 /TP
|
||||||
|
MSVCINCLUDES = /I $(MSVCDir)/include /I $(MSVCDir)/PlatformSDK/Include
|
||||||
|
# Using /ENTRY seems to automatically invoke /NODEFAULTLIBS.
|
||||||
|
MSVCLIBS = /link /LIBPATH:$(MSVCDir)/lib /LIBPATH:$(MSVCDir)/PlatformSDK/lib \
|
||||||
|
/ENTRY:cygloadCRTStartup uuid.lib msvcprtd.lib msvcrtd.lib \
|
||||||
|
oldnames.lib kernel32.lib
|
||||||
|
|
||||||
|
all: mingw-cygload.exe
|
||||||
|
|
||||||
|
mingw-cygload.exe: cygload.cc cygload.h
|
||||||
|
$(CC) $(CFLAGS) $< -o $@ $(LINKFLAGS)
|
||||||
|
|
||||||
|
msvc-cygload.exe: cygload.cc cygload.h
|
||||||
|
$(CL) $(MSVCCFLAGS) $(MSVCINCLUDES) $< /o $@ $(MSVCLIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f msvc-cygload.exe msvc-cygload.ilk cygload.obj \
|
||||||
|
msvc-cygload.pdb vc70.pdb mingw-cygload.exe
|
|
@ -0,0 +1,18 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
Command line parameters are:
|
||||||
|
|
||||||
|
-v Verbose output
|
||||||
|
-testinterrupts Pause for 30 seconds to allow testing command line
|
||||||
|
interrupts (^C)
|
||||||
|
-cygwin xxx Specifies an alternative DLL to load instead of
|
||||||
|
cygwin1.dll.
|
|
@ -0,0 +1,618 @@
|
||||||
|
// cygload.cpp
|
||||||
|
//
|
||||||
|
// Copyright 2005, Red Hat, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
source "site.exp"
|
||||||
|
|
||||||
|
if { ! [isnative] } {
|
||||||
|
verbose "skipping cygload because it's not native \"$target_triplet\" != \"$build_triplet\""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
proc ws_spawn {cmd args} {
|
||||||
|
global rv
|
||||||
|
verbose "running $cmd\n"
|
||||||
|
set rv {}
|
||||||
|
# First item in rv is the return code, second item is the message
|
||||||
|
lappend rv [catch "exec $cmd" message] $message
|
||||||
|
verbose send "catchCode = $rv\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_spawn "gcc -mno-cygwin $srcdir/$subdir/cygload.cpp -o mingw-cygload.exe -lstdc++ -Wl,-e,_cygloadCRTStartup@0"
|
||||||
|
|
||||||
|
if { $rv != {0 {}} } {
|
||||||
|
verbose -log "$rv"
|
||||||
|
fail "cygload (compile)"
|
||||||
|
} else {
|
||||||
|
if { $verbose } {
|
||||||
|
set redirect_output "./mingw-cygwin.log"
|
||||||
|
} else {
|
||||||
|
set redirect_output /dev/null
|
||||||
|
}
|
||||||
|
set windows_runtime_root [exec cygpath -m $runtime_root]
|
||||||
|
ws_spawn "./mingw-cygload.exe -cygwin $windows_runtime_root/cygwin0.dll > $redirect_output"
|
||||||
|
if { $rv != {0 {}} } {
|
||||||
|
verbose -log "cygload: $rv"
|
||||||
|
fail "cygload (execute)"
|
||||||
|
} else {
|
||||||
|
pass "cygload"
|
||||||
|
}
|
||||||
|
catch { file delete "mingw-cygload.exe" } err
|
||||||
|
if { $err != "" } {
|
||||||
|
note "error deleting mingw-cygload.exe: $err"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
// cygload.h -*- C++ -*-
|
||||||
|
//
|
||||||
|
// Copyright 2005, Red Hat, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// This program has large numbers of progress messages so as to provide
|
||||||
|
// maximum information about crash locations for anyone without access to
|
||||||
|
// a Microsoft debugger.
|
||||||
|
|
||||||
|
|
||||||
|
// This file contains the basic infrastructure for connecting an MSVC
|
||||||
|
// process to Cygwin.
|
||||||
|
|
||||||
|
#ifndef __CYGLOAD_H__
|
||||||
|
#define __CYGLOAD_H__
|
||||||
|
|
||||||
|
#include <windows.h> // for GetProcAddress()
|
||||||
|
#include <functional> // for pointer_to_unary_function
|
||||||
|
#include <stdexcept> // for runtime_error
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Convert GetLastError() to a human-readable STL exception.
|
||||||
|
class windows_error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
windows_error (const char *message, const char *detail = NULL)
|
||||||
|
: runtime_error (format (GetLastError (), message, detail)) { }
|
||||||
|
windows_error(DWORD error, const char *message, const char *detail = NULL)
|
||||||
|
: runtime_error (format (error, message, detail)) { }
|
||||||
|
|
||||||
|
static std::string format (DWORD error, const char *message,
|
||||||
|
const char *detail);
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace cygwin
|
||||||
|
{
|
||||||
|
|
||||||
|
// Cygwin keeps important thread-local information at the top of the
|
||||||
|
// stack. Its DllMain-equivalent will do the right thing for any threads
|
||||||
|
// you spawn, but you need to declare one of these as the very first thing
|
||||||
|
// in your main() function so horrible things won't happen when cygwin
|
||||||
|
// overwrites your stack. This will back up the data that will be
|
||||||
|
// overwritten and restore it when the destructor is called.
|
||||||
|
class padding {
|
||||||
|
public:
|
||||||
|
padding ();
|
||||||
|
~padding ();
|
||||||
|
|
||||||
|
// Verifies that padding has been declared.
|
||||||
|
static void check ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector< char > _backup;
|
||||||
|
char *_stackbase, *_end;
|
||||||
|
|
||||||
|
// gdb reports sizeof(_cygtls) == 3964 at the time of this writing.
|
||||||
|
// This is at the end of the object so it'll be toward the bottom
|
||||||
|
// of the stack when it gets declared.
|
||||||
|
char _padding[4096];
|
||||||
|
|
||||||
|
static padding *_main;
|
||||||
|
static DWORD _mainTID;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This hooks your application up to cygwin: it loads cygwin1.dll,
|
||||||
|
// initializes it properly, grabs some important symbols, and
|
||||||
|
// spawns a thread to let you receive signals from cygwin.
|
||||||
|
class connector {
|
||||||
|
public:
|
||||||
|
connector (const char *dll = "cygwin1.dll");
|
||||||
|
~connector ();
|
||||||
|
|
||||||
|
// A wrapper around GetProcAddress() for fetching symbols from the
|
||||||
|
// cygwin DLL. Can throw windows_error.
|
||||||
|
template < class T > void get_symbol (const char *name, T &fn) const;
|
||||||
|
|
||||||
|
// Wrappers for errno() and strerror().
|
||||||
|
int err_no () const;
|
||||||
|
std::string str_error (int) const;
|
||||||
|
|
||||||
|
// Converting between the worlds of Windows and Cygwin.
|
||||||
|
std::string unix_path (const std::string &windows) const;
|
||||||
|
std::string win_path (const std::string &unix) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
HMODULE _library;
|
||||||
|
|
||||||
|
int *(*_errno) ();
|
||||||
|
const char *(*_strerror) (int);
|
||||||
|
void (*_conv_to_full_posix_path) (const char *, char *);
|
||||||
|
void (*_conv_to_full_win32_path) (const char *, char *);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// The constructor will automatically hook you up for receiving
|
||||||
|
// cygwin signals. Just specify a signal and pass in a signal_handler.
|
||||||
|
typedef std::pointer_to_unary_function<int,void> signal_handler;
|
||||||
|
signal_handler *set_handler (int signal, signal_handler *);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Cygwin signals can only be received in threads that are calling
|
||||||
|
// interruptible functions or otherwise ready to intercept signals, so
|
||||||
|
// we spawn a thread that does nothing but call sigwait().
|
||||||
|
|
||||||
|
// This is the entry point:
|
||||||
|
static DWORD WINAPI signal_thread (void *);
|
||||||
|
// It runs this:
|
||||||
|
void await_signal ();
|
||||||
|
// And will execute this on receipt of any signal for which it's
|
||||||
|
// registered:
|
||||||
|
void handle_signals (int);
|
||||||
|
|
||||||
|
HANDLE _signal_thread;
|
||||||
|
bool _waiting_for_signals, _signal_thread_done;
|
||||||
|
CRITICAL_SECTION _thread_mutex;
|
||||||
|
|
||||||
|
typedef std::map< int, signal_handler * > callback_list;
|
||||||
|
callback_list _signal_handlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> void connector::get_symbol (const char *name,
|
||||||
|
T &symbol) const
|
||||||
|
{
|
||||||
|
FARPROC retval = NULL;
|
||||||
|
|
||||||
|
retval = GetProcAddress (_library, name);
|
||||||
|
|
||||||
|
if (retval == NULL)
|
||||||
|
throw windows_error ("GetProcAddress", name);
|
||||||
|
|
||||||
|
symbol = reinterpret_cast < T > (retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cygwin::error converts errno to a human-readable exception.
|
||||||
|
class error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
error (connector *c, const char *function, const char *detail = NULL)
|
||||||
|
: runtime_error (format (c, c->err_no (), function, detail)) { }
|
||||||
|
error (connector *c, int err_no, const char *function,
|
||||||
|
const char *detail = NULL)
|
||||||
|
: runtime_error (format (c, err_no, function, detail)) { }
|
||||||
|
|
||||||
|
static std::string format(connector *c, int err_no,
|
||||||
|
const char *message, const char *detail);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __CYGLOAD_H__
|
Loading…
Reference in New Issue