mirror of
https://git.savannah.gnu.org/git/make.git
synced 2025-02-07 06:47:52 +00:00
load.c (load_object, load_file): Accept an additional argument DLP and return in it a pointer that can be used to unload the dynamic object. read.c (eval): Call load_file with an additional argument, and record the pointer returned there in the 'struct file' object of dynamic objects in that object's 'struct file'. commands.c (execute_file_commands): Unload dynamic objects before remaking them, to avoid failure to remake if the OS doesn't allow overwriting objects that are in use. filedef.h (struct file): New member dlopen_ptr. gnumake.h (GMK_EXPORT): Define to dllexport/dllimport decorations for Windows and to nothing on other platforms. (gmk_eval, gmk_expand, gmk_add_function): Add GMK_EXPORT qualifier to prototypes. makeint.h (MAIN): Define before including gnumake.h, to give correct dllexport decorations to exported functions. (load_file): Adjust prototype. loadapi.c: Don't include gnumake.h, since makeint.h already includes it, and takes care of defining MAIN before doing so. build_w32.bat (LinkGCC): Produce an import library for functions exported by Make for loadable dynamic objects. w32/compat/posixfcn.c (dlclose): New function. w32/include/dlfcn.h (dlclose): Add prototype. scripts/features/load: Fix signatures of testload_gmk_setup and explicit_setup, to bring them in line with the documentation.
354 lines
8.1 KiB
C
354 lines
8.1 KiB
C
/* Replacements for Posix functions and Posix functionality for MS-Windows.
|
|
|
|
Copyright (C) 2013 Free Software Foundation, Inc.
|
|
This file is part of GNU Make.
|
|
|
|
GNU Make is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation; either version 3 of the License, or (at your option) any later
|
|
version.
|
|
|
|
GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include <string.h>
|
|
#include <io.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <windows.h>
|
|
|
|
#include "dlfcn.h"
|
|
|
|
#include "makeint.h"
|
|
#include "job.h"
|
|
|
|
#ifdef OUTPUT_SYNC
|
|
/* Support for OUTPUT_SYNC and related functionality. */
|
|
|
|
/* Emulation of fcntl that supports only F_GETFD and F_SETLKW. */
|
|
int
|
|
fcntl (intptr_t fd, int cmd, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start (ap, cmd);
|
|
|
|
switch (cmd)
|
|
{
|
|
case F_GETFD:
|
|
va_end (ap);
|
|
/* Could have used GetHandleInformation, but that isn't
|
|
supported on Windows 9X. */
|
|
if (_get_osfhandle (fd) == -1)
|
|
return -1;
|
|
return 0;
|
|
case F_SETLKW:
|
|
{
|
|
void *buf = va_arg (ap, void *);
|
|
struct flock *fl = (struct flock *)buf;
|
|
HANDLE hmutex = (HANDLE)fd;
|
|
static struct flock last_fl;
|
|
short last_type = last_fl.l_type;
|
|
|
|
va_end (ap);
|
|
|
|
if (hmutex == INVALID_HANDLE_VALUE || !hmutex)
|
|
return -1;
|
|
|
|
last_fl = *fl;
|
|
|
|
switch (fl->l_type)
|
|
{
|
|
|
|
case F_WRLCK:
|
|
{
|
|
DWORD result;
|
|
|
|
if (last_type == F_WRLCK)
|
|
{
|
|
/* Don't call WaitForSingleObject if we already
|
|
own the mutex, because doing so will require
|
|
us to call ReleaseMutex an equal number of
|
|
times, before the mutex is actually
|
|
released. */
|
|
return 0;
|
|
}
|
|
|
|
result = WaitForSingleObject (hmutex, INFINITE);
|
|
switch (result)
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
/* We don't care if the mutex owner crashed or
|
|
exited. */
|
|
case WAIT_ABANDONED:
|
|
return 0;
|
|
case WAIT_FAILED:
|
|
case WAIT_TIMEOUT: /* cannot happen, really */
|
|
{
|
|
DWORD err = GetLastError ();
|
|
|
|
/* Invalidate the last command. */
|
|
memset (&last_fl, 0, sizeof (last_fl));
|
|
|
|
switch (err)
|
|
{
|
|
case ERROR_INVALID_HANDLE:
|
|
case ERROR_INVALID_FUNCTION:
|
|
errno = EINVAL;
|
|
return -1;
|
|
default:
|
|
errno = EDEADLOCK;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case F_UNLCK:
|
|
{
|
|
/* FIXME: Perhaps we should call ReleaseMutex
|
|
repatedly until it errors out, to make sure the
|
|
mutext is released even if we somehow managed to
|
|
to take ownership multiple times? */
|
|
BOOL status = ReleaseMutex (hmutex);
|
|
|
|
if (status)
|
|
return 0;
|
|
else
|
|
{
|
|
DWORD err = GetLastError ();
|
|
|
|
if (err == ERROR_NOT_OWNER)
|
|
errno = EPERM;
|
|
else
|
|
{
|
|
memset (&last_fl, 0, sizeof (last_fl));
|
|
errno = EINVAL;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
default:
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
}
|
|
default:
|
|
errno = ENOSYS;
|
|
va_end (ap);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static intptr_t mutex_handle = -1;
|
|
|
|
/* Record in a static variable the mutex handle we were requested to
|
|
use. That nameless mutex was created by the top-level Make, and
|
|
its handle was passed to us via inheritance. The value of that
|
|
handle is passed via the command-line arguments, so that we know
|
|
which handle to use. */
|
|
void
|
|
record_sync_mutex (const char *str)
|
|
{
|
|
char *endp;
|
|
intptr_t hmutex = strtol (str, &endp, 16);
|
|
|
|
if (*endp == '\0')
|
|
mutex_handle = hmutex;
|
|
else
|
|
{
|
|
mutex_handle = -1;
|
|
errno = EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Create a new mutex or reuse one created by our parent. */
|
|
intptr_t
|
|
create_mutex (void)
|
|
{
|
|
SECURITY_ATTRIBUTES secattr;
|
|
intptr_t hmutex = -1;
|
|
|
|
/* If we have a mutex handle passed from the parent Make, just use
|
|
that. */
|
|
if (mutex_handle > 0)
|
|
return mutex_handle;
|
|
|
|
/* We are the top-level Make, and we want the handle to be inherited
|
|
by our child processes. */
|
|
secattr.nLength = sizeof (secattr);
|
|
secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
|
|
secattr.bInheritHandle = TRUE;
|
|
|
|
hmutex = (intptr_t)CreateMutex (&secattr, FALSE, NULL);
|
|
if (!hmutex)
|
|
{
|
|
DWORD err = GetLastError ();
|
|
|
|
fprintf (stderr, "CreateMutex: error %lu\n", err);
|
|
errno = ENOLCK;
|
|
hmutex = -1;
|
|
}
|
|
|
|
mutex_handle = hmutex;
|
|
return hmutex;
|
|
}
|
|
|
|
/* Return non-zero if F1 and F2 are 2 streams representing the same
|
|
file or pipe or device. */
|
|
int
|
|
same_stream (FILE *f1, FILE *f2)
|
|
{
|
|
HANDLE fh1 = (HANDLE)_get_osfhandle (fileno (f1));
|
|
HANDLE fh2 = (HANDLE)_get_osfhandle (fileno (f2));
|
|
|
|
/* Invalid file descriptors get treated as different streams. */
|
|
if (fh1 && fh1 != INVALID_HANDLE_VALUE
|
|
&& fh2 && fh2 != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (fh1 == fh2)
|
|
return 1;
|
|
else
|
|
{
|
|
DWORD ftyp1 = GetFileType (fh1), ftyp2 = GetFileType (fh2);
|
|
|
|
if (ftyp1 != ftyp2
|
|
|| ftyp1 == FILE_TYPE_UNKNOWN || ftyp2 == FILE_TYPE_UNKNOWN)
|
|
return 0;
|
|
else if (ftyp1 == FILE_TYPE_CHAR)
|
|
{
|
|
/* For character devices, check if they both refer to a
|
|
console. This loses if both handles refer to the
|
|
null device (FIXME!), but in that case we don't care
|
|
in the context of Make. */
|
|
DWORD conmode1, conmode2;
|
|
|
|
/* Each process on Windows can have at most 1 console,
|
|
so if both handles are for the console device, they
|
|
are the same. We also compare the console mode to
|
|
distinguish between tsdin and stdout/stderr. */
|
|
if (GetConsoleMode (fh1, &conmode1)
|
|
&& GetConsoleMode (fh2, &conmode2)
|
|
&& conmode1 == conmode2)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* For disk files and pipes, compare their unique
|
|
attributes. */
|
|
BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
|
|
|
|
/* Pipes get zero in the volume serial number, but do
|
|
appear to have meaningful information in file index
|
|
attributes. We test file attributes as well, for a
|
|
good measure. */
|
|
if (GetFileInformationByHandle (fh1, &bhfi1)
|
|
&& GetFileInformationByHandle (fh2, &bhfi2))
|
|
return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
|
|
&& bhfi1.nFileIndexLow == bhfi2.nFileIndexLow
|
|
&& bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
|
|
&& bhfi1.dwFileAttributes == bhfi2.dwFileAttributes);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif /* OUTPUT_SYNC */
|
|
|
|
#if MAKE_LOAD
|
|
|
|
/* Support for dynamic loading of objects. */
|
|
|
|
|
|
static DWORD last_err;
|
|
|
|
void *
|
|
dlopen (const char *file, int mode)
|
|
{
|
|
char dllfn[MAX_PATH], *p;
|
|
HANDLE dllhandle;
|
|
|
|
if ((mode & ~(RTLD_LAZY | RTLD_NOW | RTLD_GLOBAL)) != 0)
|
|
{
|
|
errno = EINVAL;
|
|
last_err = ERROR_INVALID_PARAMETER;
|
|
return NULL;
|
|
}
|
|
|
|
if (!file)
|
|
dllhandle = GetModuleHandle (NULL);
|
|
else
|
|
{
|
|
/* MSDN says to be sure to use backslashes in the DLL file name. */
|
|
strcpy (dllfn, file);
|
|
for (p = dllfn; *p; p++)
|
|
if (*p == '/')
|
|
*p = '\\';
|
|
|
|
dllhandle = LoadLibrary (dllfn);
|
|
}
|
|
if (!dllhandle)
|
|
last_err = GetLastError ();
|
|
|
|
return dllhandle;
|
|
}
|
|
|
|
char *
|
|
dlerror (void)
|
|
{
|
|
static char errbuf[1024];
|
|
DWORD ret;
|
|
|
|
if (!last_err)
|
|
return NULL;
|
|
|
|
ret = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, last_err, 0, errbuf, sizeof (errbuf), NULL);
|
|
while (ret > 0 && (errbuf[ret - 1] == '\n' || errbuf[ret - 1] == '\r'))
|
|
--ret;
|
|
|
|
errbuf[ret] = '\0';
|
|
if (!ret)
|
|
sprintf (errbuf, "Error code %lu", last_err);
|
|
|
|
last_err = 0;
|
|
return errbuf;
|
|
}
|
|
|
|
void *
|
|
dlsym (void *handle, const char *name)
|
|
{
|
|
FARPROC addr = NULL;
|
|
|
|
if (!handle || handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
last_err = ERROR_INVALID_PARAMETER;
|
|
return NULL;
|
|
}
|
|
|
|
addr = GetProcAddress (handle, name);
|
|
if (!addr)
|
|
last_err = GetLastError ();
|
|
|
|
return (void *)addr;
|
|
}
|
|
|
|
int
|
|
dlclose (void *handle)
|
|
{
|
|
if (!handle || handle == INVALID_HANDLE_VALUE)
|
|
return -1;
|
|
if (!FreeLibrary (handle))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif /* MAKE_LOAD */
|
|
|