Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

errno/strerror() Observations

20 views
Skip to first unread message

Josh Birnbaum

unread,
Apr 6, 2005, 8:36:52 PM4/6/05
to
Hi Folks,

I'm seeing some strange behavior from strerror(). This hinges upon
the fact that errno is a global. Consider the following code in,
say, main.c:

if( ( sockfd = socket( AF_INET, SOCK_DGRAM, 0 ) ) == -1 )
{
errHandler( "socket" );
return (-1);
}

If socket fails, the errno global is set and we call errHandler().
In error.c we have:

void errHandler( char *sysCall )
{
/* some code here */

fprintf( stderr, "myprog: ERROR: %s: %s\n", sysCall, strerror( errno
) );

/* some code here */
}

My problem is that the errno value, set as a result of socket() failure,
is lost once we get to errHandler(). strerror(), as a result, reports
the incorrect error message. If, however, I define errHandler as:

void errHandler( char *sysCall, int errVal );

and call the function via:

errHandler( "socket", errno );

then strerror() returns the correct error message.

This is on Debian 3.0r1, linux-2.4.{29,30}, gcc rev 3.0.4.

Any guidance would be appreciated.

Thanks,

Josh.

Paul Pluzhnikov

unread,
Apr 6, 2005, 8:46:12 PM4/6/05
to
Josh Birnbaum <engi...@noorg.org> writes:

> void errHandler( char *sysCall )
> {
> /* some code here */

That code probably is the code that resets errno ...
Try commenting it out and see if the problem goes away.

> fprintf( stderr, "myprog: ERROR: %s: %s\n", sysCall, strerror( errno
> ) );
>

> If, however, I define errHandler as:
> void errHandler( char *sysCall, int errVal );
> and call the function via:
> errHandler( "socket", errno );
> then strerror() returns the correct error message.

So you've found the correct solution to your problem.
What more do you want?

Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.

Josh Birnbaum

unread,
Apr 6, 2005, 10:49:28 PM4/6/05
to
Hi Paul,

> Josh Birnbaum <engi...@noorg.org> writes:
>
> > void errHandler( char *sysCall )
> > {
> > /* some code here */
>
> That code probably is the code that resets errno ...
> Try commenting it out and see if the problem goes away.

I've done this and I still see the problem.

> > fprintf( stderr, "myprog: ERROR: %s: %s\n", sysCall, strerror( errno
> > ) );
> >
> > If, however, I define errHandler as:
> > void errHandler( char *sysCall, int errVal );
> > and call the function via:
> > errHandler( "socket", errno );
> > then strerror() returns the correct error message.
>
> So you've found the correct solution to your problem.

But if errno is global, I should not have to pass an additional
parameter to get the correct errno value.

> What more do you want?

This works fine under IRIX but fails under Linux. I'd like to
find out why.

Yours,

Josh.

Paul Pluzhnikov

unread,
Apr 6, 2005, 11:20:14 PM4/6/05
to
Josh Birnbaum <engi...@noorg.org> writes:

>> Try commenting it out and see if the problem goes away.
>
> I've done this and I still see the problem.

Well, something resets the errno ...

> This works fine under IRIX but fails under Linux. I'd like to
> find out why.

Hardware watchpoints to the rescue.

On x86, you can set a breakpoint on a memory location, and have
gdb stop when that value changes.

Here is an example:

$ cat junk.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

void error(const char *p)
{
fprintf(stderr, "Error in %s: %s\n", p, strerror(errno));
}

int main()
{
const char *fname = "/no/such/file";
FILE *fp = fopen(fname, "r"); // sets errno
if (!fp) {
char *p = malloc(~(0UL)); // resets errno; but pretend we don't know this
error("fopen");
}
return 0;
}
$ gcc -g junk.c && ./a.out
Error in fopen: Cannot allocate memory

So, let's pretend we expected "Error in fopen: No such file or
directory" and got something else instead. Let's find out who
resets errno.

$ gdb -q ./a.out
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x80484ef: file junk.c, line 12.
(gdb) r

Breakpoint 1, main () at junk.c:12
12 const char *fname = "/no/such/file";
(gdb) n
13 FILE *fp = fopen(fname, "r"); // sets errno
(gdb) n
14 if (!fp) {
(gdb) p errno
Cannot access memory at address 0x8

That's not very good. We can't even look at the global "errno"
variable. This is because on Linux errno is actualy #define'd to
(*(__errno_location())).

Let's see what that function returns:

(gdb) p/x __errno_location()
$1 = 0xb7ff2440
(gdb) x/x $1
0xb7ff2440: 0x00000002

This is good: 2 == ENOFILE, which is what we expected.
Let's set a watchpoint and see what code modifies that value:

(gdb) watch *(int *)$1
Hardware watchpoint 2: *(int *) $1
(gdb) c
Hardware watchpoint 2: *(int *) $1

Old value = 2
New value = 12
0x00bc1350 in _int_malloc () from /lib/tls/libc.so.6
(gdb) bt
#0 0x00bc1350 in _int_malloc () from /lib/tls/libc.so.6
#1 0x00bc025d in malloc () from /lib/tls/libc.so.6
#2 0x0804851c in main () at junk.c:15

So now we have the answer -- malloc() reset the errno.

Repeat this for your program, and the mistery should be solved.
Note that gdb will stop on the instruction that *follows* the one
that modified the value (and that instruction may be many lines
distant from the "culprit").

Villy Kruse

unread,
Apr 7, 2005, 3:22:53 AM4/7/05
to
On Thu, 07 Apr 2005 02:49:28 GMT,
Josh Birnbaum <engi...@noorg.org> wrote:


>
> But if errno is global, I should not have to pass an additional
> parameter to get the correct errno value.
>

You have to assume that errno may be modified by any library function.
Especially the stdio function will quite often modify errno even when
no error is returned. Also, signal handlers may modify errno if not
programmed carefully.


Villy

Lawrence D'Oliveiro

unread,
Apr 8, 2005, 7:12:10 AM4/8/05
to
In article <4254808E...@noorg.org>,
Josh Birnbaum <engi...@noorg.org> wrote:

>I'm seeing some strange behavior from strerror(). This hinges upon
>the fact that errno is a global.

The whole errno concept is one of the true bits of brain damage in the
UNIX family.

Interestingly, Linux kernel calls do not know about any such global:
they all return their error codes as the function result. It is the
standard glibc glue that stuffs these error codes into errno.

But then, there's no reason why you have to use such glue...

Josh Birnbaum

unread,
Apr 8, 2005, 9:33:41 AM4/8/05
to
Hi Paul,

> >> Try commenting it out and see if the problem goes away.
> >
> > I've done this and I still see the problem.
>
> Well, something resets the errno ...

It'd seem so. I wonder if it's something external to the program.

> > This works fine under IRIX but fails under Linux. I'd like to
> > find out why.
>
> Hardware watchpoints to the rescue.
>
> On x86, you can set a breakpoint on a memory location, and have
> gdb stop when that value changes.

Thanks for the code. Though all my Linux dev takes place on a
SparcStation 5, I have access to x86/Linux so I'll try it out
there.

> Repeat this for your program, and the mistery should be solved.
> Note that gdb will stop on the instruction that *follows* the one
> that modified the value (and that instruction may be many lines
> distant from the "culprit").

I'll keep this in mind. Thanks for taking the time to provide the
above.

Yours,

Josh.

Josh Birnbaum

unread,
Apr 8, 2005, 9:36:47 AM4/8/05
to
Hi Villy,

> > But if errno is global, I should not have to pass an additional
> > parameter to get the correct errno value.
> >
>
> You have to assume that errno may be modified by any library function.

Yes, I was thinking that it may be something external to the program.

> Especially the stdio function will quite often modify errno even when
> no error is returned.

That's interesting.

> Also, signal handlers may modify errno if not programmed carefully.

I'll keep that in mind as I do call sigset() to install 2 handlers.

Thanks,

Josh.

> Villy

Paul Pluzhnikov

unread,
Apr 8, 2005, 10:35:15 AM4/8/05
to
Josh Birnbaum <engi...@noorg.org> writes:

>> Hardware watchpoints to the rescue.
>

> Thanks for the code. Though all my Linux dev takes place on a
> SparcStation 5,

Recent versions of gdb support hardware watchpoints on Solaris/SPARC
as well. Don't know about Linux/SPARC.

Nils O. Selåsdal

unread,
Apr 9, 2005, 6:55:31 AM4/9/05
to
On Thu, 07 Apr 2005 02:49:28 +0000, Josh Birnbaum wrote:

> Hi Paul,
>
>> Josh Birnbaum <engi...@noorg.org> writes:
>>
>> > void errHandler( char *sysCall )
>> > {
>> > /* some code here */
>>
>> That code probably is the code that resets errno ...
>> Try commenting it out and see if the problem goes away.
>
> I've done this and I still see the problem.
>
>> > fprintf( stderr, "myprog: ERROR: %s: %s\n", sysCall, strerror( errno
>> > ) );
>> >
>> > If, however, I define errHandler as:
>> > void errHandler( char *sysCall, int errVal );
>> > and call the function via:
>> > errHandler( "socket", errno );
>> > then strerror() returns the correct error message.
>>
>> So you've found the correct solution to your problem.
>
> But if errno is global, I should not have to pass an additional
> parameter to get the correct errno value.
>
>> What more do you want?
>
> This works fine under IRIX but fails under Linux. I'd like to
> find out why.

Please post A FULL WORKING example that exhibits this behavior.
This shall - and does indeed work.

0 new messages