errno definition broken in non-threaded code

Jim Blandy jimb at codesourcery.com
Sat Sep 23 06:02:09 UTC 2006


I'm beginning to think libc_hidden_proto and libc_hidden_def can't be
used for errno in non-threaded configurations, without a change to the
way errno is defined for user code.  Trunk revision r13360 may not be
quite right.

On the trunk, configured with no thread support, I'm seeing a number
of test failures related to errno not being set properly.  For
instance, test/inet/bug-if1.c fails, saying that errno is zero instead
of the expected value, ENXIO.  (My target is ARM Linux, but as far as
I can see that shouldn't matter.)

For fun, I applied the following patch:

*** test/inet/bug-if1.c.~1~     2006-09-18 15:24:07.469009000 -0700
--- test/inet/bug-if1.c 2006-09-22 20:34:51.300081000 -0700
***************
*** 41,46 ****
--- 41,47 ----
        char errbuf1[256];
        char errbuf2[256];

+       fprintf (stderr, "jimb: bug-if1.c: &errno = %p\n", &errno);
        printf ("errno = %d (%s), expected %d (%s)\n",
              err, strerror_r (err, errbuf1, sizeof (errbuf1)),
              ENXIO, strerror_r (ENXIO, errbuf2, sizeof (errbuf2)));
*** libc/inet/if_index.c.~1~    2006-09-18 15:24:26.355807000 -0700
--- libc/inet/if_index.c        2006-09-22 20:36:32.099494000 -0700
***************
*** 322,333 ****
--- 322,337 ----
      {
        int serrno = errno;
        close_not_cancel_no_status (fd);
+       fprintf (stderr, "jimb: ioctl failed: %d\n", serrno, strerror (serrno));
+       fprintf (stderr, "jimb: if_index.os: &errno = %p\n", &errno);
        if (serrno == ENODEV)
        /* POSIX requires ENXIO.  */
        serrno = ENXIO;
        __set_errno (serrno);
        return NULL;
    }
+   else
+     fprintf (stderr, "jimb: ioctl succeeded\n");
    close_not_cancel_no_status (fd);

    return strncpy (ifname, ifr.ifr_name, IFNAMSIZ);

With that applied, the test fails as follows:

jimb: ioctl failed: 19
jimb: if_index.os: &errno = 0x4005c388
jimb: bug-if1.c: &errno = 0x113ec
errno = 0 (Success), expected 6 (No such device or address)

Looking at 'readelf -s | grep errno' applied to the executable and the
uClibc shared library itself, it's pretty clear that they're each
using their own copy of errno.

But this is what you'd expect.  Given uClibc's emphasis on size and
only paying for the features you're using, errno should be a plain old
int variable in the non-threaded case.  (Or at least, it's plausible
enough that I've been assuming it's a deliberate decision.)  But that
means that executables will always define their own copy of errno.
And that means that uClibc's references to errno must go through a GOT
entry.

In other words, you can't use the libc_hidden_proto / libc_hidden_def
trick to avoid dynamic relocations for errno: errno's location simply
isn't known at static link time.

Well, that's over-stating things.  You could --- by having errno be a
macro for user code that goes through __errno_location, even in
non-threaded configurations.  User code would call a function instead
of fetching a GOT entry; uClibc code would assign directly to its
hidden copy of errno, instead of going through a GOT entry.  Assuming
most system calls succeed, and thus errno is rarely referenced, it's
more a code size issue than a speed issue; without experimenting, I
wouldn't really want to guess which option was smaller.

Either way, at the moment the uClibc trunk is in the nether world
between the two options: uClibc itself doesn't go through the GOT to
assign to errno, but user code defines its own copy of errno.

It seems to me the same argument applies to any variable that is
modified by either uClibc or user code for the other to see, like
h_errno and the resolver state.



More information about the uClibc mailing list