Subject: strerror that reports win32 errnos (was Re: Windows API calls that don't work?)
From: [email-masked] (Jim Balter)

24 Mar 1997 21:51:38 -0800
Approved: [email-masked]
Distribution: cygnus
Message-ID: <[email-masked]>
References: <[email-masked]>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
X-Mailer: Mozilla 3.01Gold (WinNT; I)
Original-To: Mikey <[email-masked]>
Original-CC: Colin Peters <[email-masked]>,
        cygnus <[email-masked]>
Original-Sender: [email-masked]

Mikey wrote:
> 
> In answer to your last question, ld -s (strips) an executable incorrectly,
> and from what I understand makes it unusable on NT, you keep getting not
> owner errors,

And the reason that it's "not owner" is that cygwin.dll defaults to
EPERM when GetLastError() returns something it doesn't understand.  To
get something better:

The following modified version of seterrno from syscalls.cc sets errno
to 32000000 + the win32 errno instead of to EPERM, and fixes two bugs:
ANDing GetLastError() with too small a mask, and running off the end of
the errmap (I guess it just happens to be followed by a zero for the old
code to work):

/* Set `errno' based on GetLastError ().  */
void
seterrno (const char *file, int line)
{
  // The oldcode used 0xFF, which is wrong.
  // Even the 0xFFFF probably isn't needed.
  int win32err = GetLastError () & 0xFFFF;

  for (int i = 0; i < sizeof errmap / sizeof errmap[0]; i++)
    if (win32err == errmap[i].w)
      {
	syscall_printf ("%s:%d %s -> errno %d\n",
			file, line, errmap[i].s, errmap[i].e);
	set_errno (errmap[i].e);
	return;
      }

  syscall_printf ("%s:%d seterrno: unknown win32 error %d!!\n",
		  file, line, win32err);
  set_errno (32000000 + win32err);
}

Here's a rewrite of strerror.cc that returns a message that includes the
errno when the errno is unknown, instead of returning the previous
message returned, possibly NULL (clearly not intended but the result of
a careless #if 0) and to use sys_err and sys_nerr instead of its own
switch:

// ANSI C pedantry: The standard says that strerror must act as though
// no libc routine calls it, which includes perror.  By calling an
// internal routine and passing in a buffer, we avoid perror sharing the
// one used by strerror.
// XXX perror in newlib still calls strerror, not __error_string

#define ERRHEAD "Error"

#define STRERROR_BUFSIZE sizeof ERRHEAD + 12

extern char * sys_errlist[]; 
extern int sys_nerr;

extern "C" char *
__error_string (int errnum, char *buf)
{
  if (0 <= errnum && errnum < sys_nerr) return sys_errlist[errnum];

  __small_sprintf (buf, ERRHEAD " %d", errnum);
  return buf;
}

char *
strerror (int errnum)
{
  static char buf[STRERROR_BUFSIZE];
  return __error_string (errnum, buf);
}

To make that work, add libcerr.o to DLL_OFILES in Makefile.in (formerly
it only went into libcygwin.a, but now strerror in cygwin.dll needs it).

And finally, fix libcerr.cc by adding the missing entry for errno 0:

/*	     0		*/ "No error",

The upshot of this is that, when I run a stripped executable from bash,
it now says

bash: ./foo: Error 32000193

and I can look up 193 in WINERROR.H or Windows32/Errors.h and see it is
ERROR_BAD_EXEC_FORMAT.  Something else one can do, if they don't mind
the bloat, is to add a table or switch to strerror.cc to actually print
out the win32 error message string.  Or it could at least notice that
the errno is 32...... and generate "win32 error ..." instead
(I didn't bother, but would for something of product quality).

--
<J Q B>
-
For help on using this list, send a message to
"[email-masked]" with one line of text: "help".