1

Per traditional POSIX, errno is simply an integer lvalue, which works perfectly well with fork, but oviously doesn't work nearly as well with threads. As per pthreads, errno is a thread-local integer lvalue. Under Linux/NTPL, as an implementation detail, errno is some "macro that expands to a function returning an integer lvalue".

On my Debian system, this seems to be *__errno_location (), on some other systems I've seen things like &(gettib()->errnum.

TL;DR
Assuming I've used clone to create a thread, can I just call errno and expect that it will work, or do I have to do some special rain dance? For example, do I need to read some special field in the thread information block, or some special TLS value, or, do I get to set the address of the thread-local variable where the glibc stores the error values somehow? Something like __set_errno_location() maybe?

Or, will it "just work" as it is?

Inevitably, someone will be tempted to reply "simply use phtreads" -- please don't. I do not want to use pthreads. I want clone. I do not want any of the ill-advised functionality of pthreads, and I do not want to deal with any of its quirks, nor do I want the overhead to implement those quirks. I recognize that much of the crud in pthreads comes from the fact that it has to work (and, surprisingly, it successfully works) amongst others for some completely broken systems that are nearly three decades old, but that doesn't mean that it is necessarily a good thing for everyone and every situation. Portability is not of any concern in this case.
All I want in this particular situation is fire up another process running in the same address space as the parent, synchronization via a simple lock (say, a futex), and write working properly (which means I also have to be able to read errno correctly). As little overhead as possible, no other functionality or special behavior needed or even desired.

Damon
  • 67,688
  • 20
  • 135
  • 185
  • Don't use `clone` (or `futex`) syscall; it is essentially reserved to those *implementing* thread libraries. Just use [pthread](https://computing.llnl.gov/tutorials/pthreads) (without `clone` or `futex` ...) – Basile Starynkevitch Nov 10 '13 at 18:23
  • OK, thank you for the hint. I am familiar with pthread, and as stated, I am explicitly not interested in using pthread. I want to use `clone`, and I have a reliably working `futex` wrapper. My only problem is that I cannot find information as to what to do with `errno`. – Damon Nov 10 '13 at 20:10
  • It is a system specific implementation detail. Study Linux [x86-64 ABI](http://www.x86-64.org/documentation_folder/abi.pdf) and look inside the source of the kernel, and of some libc, like [MUSL libc](http://musl-libc.org/); you'll need to write some assembler code. – Basile Starynkevitch Nov 10 '13 at 20:47

2 Answers2

4

According to the glibc source code, errno is defined as a thread-local variable. Unfortunately, this requires significant C library support. Any threads created using pthread_create() will be made aware of thread-local variables. I would not even bother trying to get glibc to accept your foreign threads.

An alternative would be to use a different libc implementation that may allow you to extract some of its internal structures and manually set the thread control block if errno is part of it. This would be incredibly hacky and unreliable. I doubt you'll find anything like __set_errno_location(), but rather something like __set_tcb().

#include <bits/some_hidden_file.h>

void init_errno(void)
{
    struct __tcb* tcb;

    /* allocate a dummy thread control block (malloc may set errno
     * so might have to store the tcb on stack or allocate it in the
     * parent) */
    tcb = malloc(sizeof(struct __tcb));

    /* initialize errno */
    tcb->errno = 0;

    /* set pointer to thread control block (x86) */
    arch_prctl(ARCH_SET_FS, tcb);
}

This assumes that the errno macro expands to something like: ((struct __tcb*)__read_fs())->errno.

Of course, there's always the option of implementing an extremely small subset of libc yourself. Or you could write your own implementation of the write() system call with a custom stub to handle errno and have it co-exist with the chosen libc implementation.

#define my_errno /* errno variable stored at some known location */

ssize_t my_write(int fd, const void* buf, size_t len)
{
    ssize_t ret;

    __asm__ (
        /* set system call number */
        /* set up parameters */
        /* make the call */
        /* retrieve return value in c variable */
    );

    if (ret >= -4096 && ret < 0) {
        my_errno = -ret;
        return -1;
    }

    return ret;
}

I don't remember the exact details of GCC inline assembly and the system call invocation details vary depending on platform.

Personally, I'd just implement a very small subset of libc, which would just consist of a little assembler and a few constants. This is remarkably simple with so much reference code available out there, although it may be overambitious.

haste
  • 1,441
  • 1
  • 10
  • 21
  • I kind of feared it would turn out to be not all easy. You may be right that implementing my own wrapper around the one syscall (or maybe two) that I need is probably easiest. – Damon Nov 11 '13 at 10:06
0

If errno is a thread local variable, so clone() will copy it in the new process's address space? i had overrode the errno_location() function like around 2001 to use an errno based on the pid.

http://tamtrajnana.blogspot.com/2012/03/thread-safety-of-errno-variable.html

since errno is now defined as "__thread int errno;" (see above comment) this explains how __thread types are handled: Linux's thread local storage implementation

Community
  • 1
  • 1
Matt W.
  • 31
  • 2
  • This should be a comment and not an answer. – Burhan Khalid Nov 18 '13 at 05:03
  • It's both a comment and an answer. – Matt W. Nov 18 '13 at 05:10
  • Unluckily, that does not seem to be the case. It was something I had hoped for, however, a little test program that sets `errno` in the clone-child will also set it in the main thread. `errno_location()` outputs the same address from both parent and clone-child, too. So it is not "magically thread-local", you apparently need to do some unspecified/secret/internal setup (which presumably happens inside `pthread_create`). You said you _overrode_ `errno_location`, how did you do that? It's trivial on the application side (simple macro!), but how to override it for libc? – Damon Nov 18 '13 at 12:30
  • __erno_location() was defined as "weak" in the libc source. so it was as easy as line 104 here: `// WARNING: you MUST compile with -DREENTRANT for this to work int *__errno_location (void) { long pid = (long) getpid(); //if ( pid == s_pid ) return &g_errno; if ( pid <= (long)MAX_PID ) return &s_errnos[pid]; s_bad++; s_badPid = pid; return &s_errno; }` But then they changed libc so that it was no longer a weak function and you couldn't do that any more. Then they added the __thread thread local variable type. are you using an old version of libc? – Matt W. Nov 19 '13 at 04:35
  • you're right. i have the same problem with the new glibc. maybe we do need to look at pthread_create() source code. – Matt W. Nov 19 '13 at 09:12
  • let me know if you find anything, because i am interested as well. – Matt W. Nov 20 '13 at 05:25