This is the mail archive of the
cygwin-developers
mailing list for the Cygwin project.
Re: CFA: pseudo-reloc v2
- From: Charles Wilson <cygwin at cwilson dot fastmail dot fm>
- To: cygwin-developers at cygwin dot com
- Date: Mon, 05 Oct 2009 01:07:51 -0400
- Subject: Re: CFA: pseudo-reloc v2
- References: <4AC7910E.1010900@cwilson.fastmail.fm>
Corinna wrote:
> Here's another idea. At one point Brian Dessent proposed a patch to
> detect a missing DLL. Hmm, yes, here's the checkin of your patch
> implementing that, back on 2008-03-26.
>
> I was wondering if a similar technique could be used to print an error
> message in the parent process, if the relocation failed in the child
> process. The idea would be to generate a specific status code which
> would then catched in status_exit, just like STATUS_DLL_NOT_FOUND.
There are three error cases under consideration:
#1) invalid pseudo_reloc version number. Right now, this uses printf
(ick!). If we go Corinna's route (with or without also using
WriteConsole) -- how about STATUS_ILLEGAL_DLL_RELOCATION?
#2) invalid address (we can't access the specified page), which is
currently checked by 'assert (VirtualQuery (addr, &b, sizeof(b)));'.
For this, how about STATUS_NOT_MAPPED_VIEW (0xC0000019, which needs to
be added to ntdll.h). Aocording to:
http://msdn.microsoft.com/en-us/library/cc704588(PROT.10).aspx
this code means "The address range to unmap is not a mapped view." (Ok,
we're not actually trying to unmap, but VirtualQuery doesn't set an
error number on 'error' -- it just returns 0. But the only way that
would happen, I think, is if the address itself isn't mapped --
hence...NOT_MAPPED_VIEW.
Unless we want to use STATUS_ILLEGAL_DLL_RELOCATION for this, too --
which might be the smarter way to go, since then we can catch both cases
with the same code in sigproc.c and pinfo.c.
#3) there's also a non-fatal DEBUG printf.
One downside of using WriteConsole[W] is that you'll never see the
message if you're running in a terminal other than cmd.exe, nor if
you're launching the app via a shortcut. The output will go to the
"invisible" console allocated by cygwin, right?
> I'm just not sure how to generate that specific status code...
Yeah, that...
cgf suggests ExitProcess. I tried that, but the process hangs when it
calls that function. I think this has to do with the fact that
ExitProcess goes back thru the loaded DLL list and tries to call the
entry points with DLL_PROCESS_DETACH. But, since the (current) DLL is
fubared...
Now, TerminateProcess seems to work -- but then NONE of the DLLs get
notified. Oh -- and it hangs if you strace within any terminal except
cmd.exe; the strace output is identical in the two cases, but the
non-cmd one has this extra line at the end:
29661 30435 [sig] crtest_v1 4692 wait_sig: entering ReadFile loop,
my_readsig 0x17C, my_sendsig 0x180
and then it hangs.
Wierd.
> Other than that, I have no problems to use WriteConsole.
So...maybe something like the following? This is a diff against the
current mingw/pseudo-reloc.c. The "normal" code path (that is, none of
the error conditions are hit) works fine.
The rest of this email is about the error cases.
I tested the error path by reversing the test at line 110. In an rxvt
window, I got the following behavior:
$ ./crtest_v2.exe
$ echo $?
0
In a cmd.exe window, this is what I observerd:
$ ./crtest_v2.exe
Relocation error: bad address specified 0x00401139.
$ echo $?
0
I also attempted to modify pinfo.cc and sigproc.cc bits following Dave's
suggestion, partly so that these reloc errors would result in a 127 exit
code, just like cgf/brian's mod for STATUS_DLL_NOT_FOUND. But I
couldn't get it to "trigger" -- I didn't get the error message I
specified in pinfo.cc, and I still got an exit code 0, not 127. Then, I
tried this:
--------------- foo.c -----------------
#include <stdio.h>
#include <windows.h>
#include <ntdef.h>
#define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xc0000269)
#define STATUS_DLL_NOT_FOUND ((NTSTATUS) 0xc0000135)
int main(int argc, char* argv[])
{
TerminateProcess (GetCurrentProcess(), STATUS_ILLEGAL_DLL_RELOCATION);
exit (1);
}
---------------------------------------
With cygwin1.dll having the modified pinfo/sigproc, and launching this
test program from bash, I would *expect* to see the message from my
patched pinfo.cc: "foo.exe: error while loading shared libraries: ?:
cannot open shared object file: illegal relocation" (there's a '?'
because this silly test program doesn't have an 'unloaded DLL' name to
put there).
If I understand the pinfo code, it's actually the *parent* process that
receives the err code >= 0xc0000000, and prints the
DLL_NOT_FOUND/ILLEGAL_DLL_RELOCATION error message. That is, bash.
However:
$ gcc -o foo.exe foo.c
$ ./foo.exe
$ echo $?
0
(NOT 127, and NOT 1 !)
But, if I use gcc-3 -mno-cygwin:
$ gcc-3 -mno-cygwin -o foo.exe foo.c
$ ./foo.exe
/usr/src/devel/kernel/pseudo-reloc-tests/test-app/foo.exe: error while
loading shared libraries: ?: cannot open shared object file: illegal
relocation
$ echo $?
127
which is what I expected to see in all cases. Just to verify that it is
not a gcc-3/gcc-4 thing:
$ gcc-3 -o foo.exe foo.c
$ ./foo.exe
$ echo $?
0
So, it's a "cygwin" thing: if the child is a cygwin app, then the
pinfo::maybe_set_exit_code_from_windows --> status_exit() code path is
NOT hit at all. That's just weird. Help?
--
Chuck
--- winsup/mingw/pseudo-reloc.c 2009-03-17 09:18:08.463300000 -0400
+++ winsup/cygwin/lib/pseudo-reloc.c 2009-10-05 01:00:31.362000000 -0400
@@ -20,6 +20,17 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+
+#if defined(__CYGWIN__)
+#include <wchar.h>
+#include <ntdef.h>
+#include <stdarg.h>
+/* copied from winsup.h */
+# define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy")))
+#define STATUS_ILLEGAL_DLL_RELOCATION ((NTSTATUS) 0xc0000269)
+#else
+# define NO_COPY
+#endif
extern char __RUNTIME_PSEUDO_RELOC_LIST__;
extern char __RUNTIME_PSEUDO_RELOC_LIST_END__;
@@ -42,6 +53,51 @@
DWORD version;
} runtime_pseudo_reloc_v2;
+#if defined(__CYGWIN__)
+#define MAX_WRITE_CHARS 16384
+
+/* This function is adapted from cygwin fhandler_console.cc, so
+ * I'm not sure it can be included in this "not copyrighted" file
+ */
+static int
+__write_console (HANDLE h, PWCHAR buf, DWORD len, DWORD *done)
+{
+ while (len > 0)
+ {
+ DWORD nbytes = len > MAX_WRITE_CHARS ? MAX_WRITE_CHARS : len;
+ if (!WriteConsoleW (h, buf, nbytes, done, 0))
+ {
+ /* don't bother with errno here; no-one will check it */
+ return FALSE;
+ }
+ len -= *done;
+ buf += *done;
+ }
+ return TRUE;
+}
+
+#define SHORT_MSG_BUF_SZ 64
+static int
+__print_reloc_error (const wchar_t *fmt, ...)
+{
+ WCHAR buf[SHORT_MSG_BUF_SZ];
+ DWORD len;
+ DWORD done;
+ va_list args;
+ HANDLE e = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (e == INVALID_HANDLE_VALUE)
+ return 0;
+
+ va_start (args, fmt);
+ len = (DWORD) vswprintf (buf, SHORT_MSG_BUF_SZ, fmt, args);
+ va_end (args);
+
+ buf[SHORT_MSG_BUF_SZ-1] = L'\0'; /* paranoia */
+ return __write_console (e, buf, len, &done);
+}
+#endif /* __CYGWIN__ */
+
static void
__write_memory (void *addr,const void *src,size_t len)
{
@@ -49,7 +105,19 @@
DWORD oldprot;
if (!len)
return;
+
+#if defined(__CYGWIN__)
+ if (!VirtualQuery (addr, &b, sizeof(b)))
+ {
+ __print_reloc_error (
+ L"Relocation error: bad address specified 0x%08x.",
+ addr);
+ TerminateProcess (GetCurrentProcess(), STATUS_ILLEGAL_DLL_RELOCATION);
+ }
+#else
assert (VirtualQuery (addr, &b, sizeof(b)));
+#endif
+
/* Temporarily allow write access to read-only protected memory. */
if (b.Protect != PAGE_EXECUTE_READWRITE && b.Protect != PAGE_READWRITE)
VirtualProtect (b.BaseAddress, b.RegionSize, PAGE_EXECUTE_READWRITE,
@@ -95,10 +163,17 @@
/* Check if this is a known version. */
if (v2_hdr->version != RP_VERSION_V2)
{
-#ifdef DEBUG
+#if defined(__CYGWIN__)
+ __print_reloc_error (
+ L"Relocation error: invalid pseudo_reloc version %d.",
+ (int) v2_hdr->version);
+ TerminateProcess (GetCurrentProcess(), STATUS_ILLEGAL_DLL_RELOCATION);
+#else
+# if defined(DEBUG)
fprintf (stderr, "internal mingw runtime error:"
"psuedo_reloc version %d is unknown to this runtime.\n",
(int) v2_hdr->version);
+# endif
#endif
return;
}
@@ -139,9 +214,15 @@
default:
reldata=0;
#ifdef DEBUG
+# if defined(__CYGWIN__)
+ __print_reloc_error (
+ L"Relocation error: unknown pseudo_reloc bit size %d.",
+ (int) (r->flags & 0xff));
+# else
fprintf(stderr, "internal mingw runtime error: "
"unknown pseudo_reloc bit size %d\n",
(int) (r->flags & 0xff));
+# endif
#endif
break;
}
@@ -170,7 +251,7 @@
void
_pei386_runtime_relocator ()
{
- static int was_init = 0;
+ static NO_COPY int was_init = 0;
if (was_init)
return;
++was_init;
Index: pinfo.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/pinfo.cc,v
retrieving revision 1.253
diff -u -p -r1.253 pinfo.cc
--- pinfo.cc 12 Jul 2009 21:15:47 -0000 1.253
+++ pinfo.cc 5 Oct 2009 05:05:35 -0000
@@ -128,6 +128,16 @@ status_exit (DWORD x)
x = 127;
}
break;
+ case STATUS_ILLEGAL_DLL_RELOCATION:
+ {
+ char posix_prog[NT_MAX_PATH];
+ path_conv pc (myself->progname, PC_NOWARN);
+ mount_table->conv_to_posix_path (pc.get_win32 (), posix_prog, 1);
+ small_printf ("%s: error while loading shared libraries: %s: cannot open shared object file: illegal relocation\n",
+ posix_prog, find_first_notloaded_dll (pc));
+ x = 127;
+ }
+ break;
default:
x = 127;
}
Index: sigproc.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/sigproc.cc,v
retrieving revision 1.317
diff -u -p -r1.317 sigproc.cc
--- sigproc.cc 2 Aug 2009 21:38:40 -0000 1.317
+++ sigproc.cc 5 Oct 2009 05:05:35 -0000
@@ -923,6 +923,8 @@ child_info::proc_retry (HANDLE h)
break;
case STATUS_DLL_NOT_FOUND:
return exit_code;
+ case STATUS_ILLEGAL_DLL_RELOCATION:
+ return exit_code;
case STATUS_CONTROL_C_EXIT:
if (saw_ctrl_c ())
return EXITCODE_OK;